This file is used to prepare the figures for the paper.

library(dplyr)
library(patchwork)
library(ggplot2)
library(ComplexHeatmap)
library(org.Mm.eg.db)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

Here are the folders where analyzes are stored :

data_dir = "./.."
list.files(data_dir)
##  [1] "0_intro"      "1_metadata"   "2_individual" "3_combined"   "4_zoom"      
##  [6] "5_wu"         "6_figures"    "LICENSE"      "index.html"   "index_layout"

We load the dataset containing all cells :

sobj = readRDS(paste0(data_dir, "/3_combined/hs_hd_sobj.rds"))
sobj
## An object of class Seurat 
## 20003 features across 12111 samples within 1 assay 
## Active assay: RNA (20003 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

These are all the samples analyzed :

sample_info = readRDS(paste0(data_dir, "/1_metadata/hs_hd_sample_info.rds"))

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

These are the custom colors for cell populations :

color_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_color_markers.rds"))
color_markers = color_markers[names(color_markers) != "melanocytes"]
ors_color = color_markers["ORS"]
color_markers["ORS"] = color_markers["IFE"] 
color_markers["IFE"] = ors_color
color_markers["B cells"] = "chocolate3"
rm(ors_color)

# re-order
color_markers = color_markers[c("CD4 T cells", "CD8 T cells", "Langerhans cells", "macrophages", "B cells",
                                "cuticle", "cortex", "medulla", "IRS", "proliferative",
                                "HFSC", "ORS", "IBL", "IFE", "sebocytes")]

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(hjust = 1, angle = 20))

We define custom colors for sample type :

sample_type_colors = setNames(nm = levels(sample_info$sample_type),
                              c("#C55F40", "#2C78E6"))

data.frame(cell_type = names(sample_type_colors),
           color = unlist(sample_type_colors)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(sample_type_colors), breaks = names(sample_type_colors)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We set a background color :

bg_color = "gray94"

This is the correspondence between cell types and cell families, and custom colors to color cells by cell family :

custom_order_cell_type = data.frame(
  cell_type = names(color_markers),
  cell_family = c(rep("immune cells", 5),
                  rep("matrix", 5),
                  rep("non matrix", 5)),
  stringsAsFactors = FALSE)
custom_order_cell_type$cell_type = factor(custom_order_cell_type$cell_type,
                                          levels = custom_order_cell_type$cell_type)
rownames(custom_order_cell_type) = custom_order_cell_type$cell_type

family_color = c("immune cells" = "slateblue1",
                 "matrix" = "mediumseagreen",
                 "non matrix" = "firebrick3")

We load markers to display on a dotplot to assess cell type annotation :

dotplot_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_dotplot_markers.rds"))
dotplot_markers = dotplot_markers[names(dotplot_markers) != "melanocytes"]
lengths(dotplot_markers)
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##                2                2                2                2 
##          B cells          cuticle           cortex          medulla 
##                2                2                2                2 
##              IRS    proliferative              IBL              ORS 
##                2                2                2                2 
##              IFE             HFSC        sebocytes 
##                2                2                2

Custom functions to display gene expression on the heatmap :

color_fun = function(one_gene) {
  gene_range = range(ht_annot[, one_gene])
  gene_palette = circlize::colorRamp2(colors = c("#FFFFFF", aquarius::color_gene[-1]),
                                      breaks = seq(from = gene_range[1], to = gene_range[2],
                                                   length.out = length(aquarius::color_gene)))
  return(gene_palette)
}

All samples

Settings

This is the projection name to visualize cells :

name2D = "harmony_38_tsne"
name2D_atlas = name2D

Preparation

We make a low resolutive clustering for the heatmap :

sobj = Seurat::FindClusters(sobj, resolution = 0.4)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 12111
## Number of edges: 475472
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.9421
## Number of communities: 17
## Elapsed time: 1 seconds
length(levels(sobj$seurat_clusters))
## [1] 17

We define cluster type and cluster family :

sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))
sobj$cluster_family = custom_order_cell_type[sobj$cluster_type, "cell_family"]
sobj$cluster_family = factor(sobj$cluster_family,
                             levels = names(family_color))

Figures

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Sample type :

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_type = sobj$sample_type
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_type)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_type_colors,
                              breaks = names(sample_type_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

grey_palette = setNames(nm = levels(sobj$seurat_clusters),
                        rep("#D9D9D9", length(levels(sobj$seurat_clusters))))
grey_palette[c("7", "16", "1", "12", "11", "10", "15")] = "#BDBDBD"
grey_palette[c("16", "14", "5", "9")] = "#969696"

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.4,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 6) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cell type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster family annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_family", cols = family_color) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cell type annotation split by condition :

plot_list = aquarius::plot_split_dimred(sobj, reduction = name2D,
                                        group_by = "cell_type",
                                        group_color = color_markers,
                                        split_by = "sample_type",
                                        split_color = sample_type_colors,
                                        bg_color = bg_color)

patchwork::wrap_plots(plot_list) &
  Seurat::NoLegend()

Gene expression to assess annotation :

genes = c("PTPRC", "MSX2", "KRT14")
names(genes) = c("immune cells", "matrix cells", "non-matrix cells")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  pop = names(genes)[gene_id]
  
  sobj$my_gene = Seurat::FetchData(sobj, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj, features = "my_gene", reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) + 
    # subtitle = pop) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

Barplot by cluster family :

quantif = table(sobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

aquarius::plot_barplot(df = table(sobj$sample_identifier,
                                  sobj$cluster_family) %>%
                         as.data.frame.table() %>%
                         `colnames<-`(c("Sample", "Cell Type", "Number")),
                       x = "Sample", y = "Number", fill = "Cell Type",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 1.05, label = .data$nb_cells),
                      label.size = 0, size = 4) +
  ggplot2::scale_fill_manual(values = unlist(family_color),
                             breaks = names(family_color),
                             name = "Cell Family") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 axis.text.x = element_text(margin = margin(t = 25, r = 0, b = 0, l = 0)),
                 legend.position = "none")

Heatmap of cluster proportion by sample :

group_by = "seurat_clusters"

cluster_by_sample = table(sobj$sample_identifier,
                          sobj@meta.data[, group_by]) %>%
  prop.table(margin = 1) %>%
  as.matrix()

## Right annotation : number of cells by dataset
ht_annot = table(sobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  `rownames<-`(.$sample_identifier) %>%
  dplyr::select(-sample_identifier)

ha_right = ComplexHeatmap::HeatmapAnnotation(
  df = ht_annot,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "top",
  col = list(nb_cells  = circlize::colorRamp2(colors = RColorBrewer::brewer.pal(name = "Greys", n = 9),
                                              breaks = seq(from = range(ht_annot$nb_cells)[1],
                                                           to = range(ht_annot$nb_cells)[2],
                                                           length.out = 9))))

## Left annotation : gender
ha_left = ComplexHeatmap::HeatmapAnnotation(
  gender = sample_info$gender,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "top",
  col = list(gender = setNames(nm = c("F", "M"),
                               c("lightcyan3", "navyblue"))))

## Top annotation : main cell type in this cluster
ht_annot = table(sobj$sample_identifier,
                 sobj@meta.data[, group_by]) %>%
  prop.table(margin = 1) %>%
  as.matrix()

ht_annot = table(sobj$cell_type,
                 sobj@meta.data[, group_by]) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
ht_annot = data.frame(row.names = names(ht_annot),
                      cell_type = names(color_markers)[ht_annot],
                      stringsAsFactors = FALSE)
ht_annot = dplyr::left_join(ht_annot, custom_order_cell_type, by = "cell_type") %>%
  # Simplification for matrix
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("medulla", "cortex", "cuticle"), yes = "hair shaft", no = cell_type)) %>%
  # Simplification for T cells
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("CD4 T cells", "CD8 T cells"), yes = "T cells", no = cell_type)) %>%
  # Simplification for APC
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("Langerhans cells", "macrophages"), yes = "APC", no = cell_type)) %>%
  # Add color
  dplyr::mutate(color = as.character(color_markers[cell_type])) %>%
  dplyr::mutate(color = ifelse(cell_type == "hair shaft", yes = "#FFB6C1", no = color)) %>%
  dplyr::mutate(color = ifelse(cell_type == "T cells", yes = "#8A6EE6", no = color)) %>%
  dplyr::mutate(color = ifelse(cell_type == "APC", yes = "#9CAA4B", no = color))

ha_top = ComplexHeatmap::HeatmapAnnotation(
  # cell_type = ht_annot$cell_type,
  cell_family = ht_annot$cell_family,
  which = "column",
  show_legend = TRUE,
  show_annotation_name = FALSE,
  # annotation_name_side = "left",
  col = list(#cell_type = setNames(nm = ht_annot$cell_type,
    #                      ht_annot$color),
    cell_family = family_color
  ))

## Assemble heatmap
ht = ComplexHeatmap::Heatmap(cluster_by_sample,
                             heatmap_legend_param = list(title = "Proportion",
                                                         col = c("#2166AC", "#F7F7F7", "#B2182B")),
                             # bottom_annotation = ha_bottom,
                             right_annotation = ha_right,
                             left_annotation = ha_left,
                             top_annotation = ha_top,
                             cluster_rows = TRUE,
                             cluster_columns = TRUE,
                             row_title = "Sample",
                             row_names_gp = grid::gpar(names = sample_info$sample_identifier,
                                                       col = sample_info$color,
                                                       fontface = "bold"),
                             column_title = "Cluster",
                             column_names_centered = TRUE,
                             row_names_side = "left",
                             column_names_side = "top",
                             column_names_rot = 0)

## Draw !
ComplexHeatmap::draw(ht, merge_legends = TRUE)

For the dotplot, we clarify clusters and cell type annotation :

cell_type_in_cluster = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 1) %>%
  apply(., 1, which.max)
cell_type_in_cluster = cell_type_in_cluster - 1

missing_cluster = setdiff(levels(sobj$seurat_clusters),
                          cell_type_in_cluster)

cell_type_in_cluster = data.frame(cell_type = c(names(cell_type_in_cluster), cluster_type[missing_cluster]),
                                  cluster_id = c(cell_type_in_cluster, names(cluster_type[missing_cluster])),
                                  stringsAsFactors = FALSE, row.names = NULL) %>%
  dplyr::mutate(cluster_id = as.numeric(cluster_id)) %>%
  dplyr::arrange(cell_type, cluster_id)

custom_order_cell_type$clusters = custom_order_cell_type %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    cell_type = one_row["cell_type"]
    clusters = cell_type_in_cluster %>%
      dplyr::filter(.data$cell_type == .env$cell_type) %>%
      dplyr::pull(cluster_id)
    
    cell_type_cluster = paste0(cell_type, " (", paste0(clusters, collapse = ", "), ")")
    
    return(cell_type_cluster)
  }) %>%
  factor(., levels = .)

custom_order_cell_type
##                         cell_type  cell_family                     clusters
## CD4 T cells           CD4 T cells immune cells              CD4 T cells (2)
## CD8 T cells           CD8 T cells immune cells          CD8 T cells (2, 14)
## Langerhans cells Langerhans cells immune cells         Langerhans cells (6)
## macrophages           macrophages immune cells              macrophages (6)
## B cells                   B cells immune cells                 B cells (16)
## cuticle                   cuticle       matrix                  cuticle (3)
## cortex                     cortex       matrix                   cortex (3)
## medulla                   medulla       matrix                 medulla (11)
## IRS                           IRS       matrix                     IRS (15)
## proliferative       proliferative       matrix proliferative (4, 9, 10, 13)
## HFSC                         HFSC   non matrix                  HFSC (7, 8)
## ORS                           ORS   non matrix                      ORS (0)
## IBL                           IBL   non matrix                      IBL (1)
## IFE                           IFE   non matrix                      IFE (5)
## sebocytes               sebocytes   non matrix               sebocytes (12)

Dotplot :

plot_list = aquarius::plot_dotplot(sobj,
                                   markers = c("PTPRC",
                                               "CD3E", "CD4",
                                               "CD3E", "CD8A",
                                               "CD207", "AIF1",
                                               "TREM2", "MSR1",
                                               "CD79A", "CD79B",
                                               # "PRDM1", "KRT85",
                                               "MSX2",
                                               "KRT32", "KRT35",
                                               "KRT31", "PRR9",
                                               "BAMBI", "ALDH1A3",
                                               "KRT71", "KRT73",
                                               "TOP2A", "MCM5",
                                               "KRT14", "CXCL14",
                                               "KRT15", "COL17A1",
                                               "DIO2", "TCEAL2",
                                               "KRT16", "KRT6C",
                                               "SPINK5", "LY6D",
                                               "CLMP", "PPARG"),
                                   assay = "RNA", column_name = "cell_type", nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "left",
                 legend.justification = "bottom",
                 legend.box = "vertical",
                 legend.box.margin = margin(0,70,0,0),
                 axis.title = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.text.x = element_blank(),
                 axis.line.x = element_blank(),
                 plot.margin = unit(rep(0, 4), "cm"))

p = ggplot2::ggplot(custom_order_cell_type, aes(x = clusters, y = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(x = 0.5, xend = 5.5, y = 0, yend = 0), size = 6, col = family_color["immune cells"]) +
  ggplot2::geom_segment(aes(x = 5.5, xend = 10.5, y = 0, yend = 0), size = 6, col = family_color["matrix"]) +
  ggplot2::geom_segment(aes(x = 10.5, xend = 15.5, y = 0, yend = 0), size = 6, col = family_color["non matrix"]) +
  ggplot2::scale_y_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.y = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.title = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 10, color = "black"),
                 plot.margin = unit(c(0,0.5,0.5,0), "cm"))

plot_list = patchwork::wrap_plots(plot_list, p,
                                  ncol = 1, heights = c(25, 1))
plot_list

Immune cells

Settings

We load the immune cells dataset :

sobj_ic = readRDS(paste0(data_dir, "/4_zoom/1_zoom_immune/immune_cells_sobj.rds"))
sobj_ic
## An object of class Seurat 
## 15121 features across 2329 samples within 1 assay 
## Active assay: RNA (15121 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/1_zoom_immune/immune_cells_list_results.rds"))

lapply(list_results, FUN = names)
## $`Langerhans cells`
## [1] "mark" "gsea"
## 
## $macrophages
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $`CD4 T cells`
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $`CD8 T cells`
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"

Preparation

We defined cluster type and cluster family :

cluster_type = table(sobj_ic$cell_type, sobj_ic$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_ic$cell_type)[cluster_type])

sobj_ic$cluster_type = cluster_type[sobj_ic$seurat_clusters]
sobj_ic$cluster_type = factor(sobj_ic$cluster_type,
                              levels = levels(sobj_ic$cell_type))

Figures

Control cells on the full atlas :

sobj$is_immune = (colnames(sobj) %in% colnames(sobj_ic))

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_immune", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(family_color[["immune cells"]], bg_color),
                              breaks = c(TRUE, FALSE)) +
  ggplot2::labs(title = "Immune cells",
                subtitle = paste0(ncol(sobj_ic), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_ic, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Violin plot of IL1B in macrophages :

subsobj = subset(sobj_ic, seurat_clusters == 2)
table(subsobj$sample_type)
## 
##  HS  HD 
## 378  31
il1b_hs = subsobj@assays$RNA@data["IL1B", subsobj$sample_type == "HS"]
il1b_hd = subsobj@assays$RNA@data["IL1B", subsobj$sample_type == "HD"]
il1b_hs_VS_il1b_hd = stats::t.test(il1b_hs, il1b_hd)
il1b_hs_VS_il1b_hd
## 
##  Welch Two Sample t-test
## 
## data:  il1b_hs and il1b_hd
## t = 2.3206, df = 35.801, p-value = 0.02612
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.05066074 0.75429252
## sample estimates:
## mean of x mean of y 
## 0.9256690 0.5231924
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "IL1B", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "IL1B", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of IL1B in macrophages :

il6_hs = subsobj@assays$RNA@data["IL6", subsobj$sample_type == "HS"]
il6_hd = subsobj@assays$RNA@data["IL6", subsobj$sample_type == "HD"]
il6_hs_VS_il6_hd = stats::t.test(il6_hs, il6_hd)
il6_hs_VS_il6_hd
## 
##  Welch Two Sample t-test
## 
## data:  il6_hs and il6_hd
## t = 2.3591, df = 377, p-value = 0.01883
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.001453521 0.016004661
## sample estimates:
##   mean of x   mean of y 
## 0.008729091 0.000000000
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "IL6", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of TNF in macrophages :

tnf_hs = subsobj@assays$RNA@data["TNF", subsobj$sample_type == "HS"]
tnf_hd = subsobj@assays$RNA@data["TNF", subsobj$sample_type == "HD"]
tnf_hs_VS_tnf_hd = stats::t.test(tnf_hs, tnf_hd)
tnf_hs_VS_tnf_hd
## 
##  Welch Two Sample t-test
## 
## data:  tnf_hs and tnf_hd
## t = 3.8395, df = 45.546, p-value = 0.0003786
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.1140962 0.3657054
## sample estimates:
##  mean of x  mean of y 
## 0.31784960 0.07794879
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "TNF", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "TNF", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of GZMA in CD4 T cells :

subsobj = subset(sobj_ic, seurat_clusters %in% c(0,10))
table(subsobj$sample_type)
## 
##  HS  HD 
## 569  90
gzma_hs = subsobj@assays$RNA@data["GZMA", subsobj$sample_type == "HS"]
gzma_hd = subsobj@assays$RNA@data["GZMA", subsobj$sample_type == "HD"]
gzma_hs_VS_gzma_hd = stats::t.test(gzma_hs, gzma_hd)
gzma_hs_VS_gzma_hd
## 
##  Welch Two Sample t-test
## 
## data:  gzma_hs and gzma_hd
## t = 14.755, df = 172.51, p-value < 2.2e-16
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  1.225578 1.604097
## sample estimates:
## mean of x mean of y 
## 1.8456108 0.4307735
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "GZMA", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of IFNG in CD4 T cells :

ifng_hs = subsobj@assays$RNA@data["IFNG", subsobj$sample_type == "HS"]
ifng_hd = subsobj@assays$RNA@data["IFNG", subsobj$sample_type == "HD"]
ifng_hs_VS_ifng_hd = stats::t.test(ifng_hs, ifng_hd)
ifng_hs_VS_ifng_hd
## 
##  Welch Two Sample t-test
## 
## data:  ifng_hs and ifng_hd
## t = 8.1978, df = 651.25, p-value = 1.303e-15
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.1874856 0.3055919
## sample estimates:
##  mean of x  mean of y 
## 0.25885178 0.01231299
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "IFNG", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of IL17A in CD4 T cells :

IL17_hs = subsobj@assays$RNA@data["IL17A", subsobj$sample_type == "HS"]
IL17_hd = subsobj@assays$RNA@data["IL17A", subsobj$sample_type == "HD"]
IL17_hs_VS_IL17_hd = stats::t.test(IL17_hs, IL17_hd)
IL17_hs_VS_IL17_hd
## 
##  Welch Two Sample t-test
## 
## data:  IL17_hs and IL17_hd
## t = 4.5667, df = 219.64, p-value = 8.255e-06
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.1412906 0.3558327
## sample estimates:
##  mean of x  mean of y 
## 0.30323619 0.05467451
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "IL17RE", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

We represent some genes split by sample type :

plot_list = lapply(c("IL1B", "GZMA", "IFNG", "IL17A", "TNF", "IL6"), FUN = function(one_gene) {
  p = aquarius::plot_split_dimred(sobj_ic,
                                  reduction = name2D,
                                  split_by = "sample_type",
                                  color_by = one_gene,
                                  color_palette = c("gray70", "#FDBB84", "#EF6548", "#7F0000", "black"),
                                  main_pt_size = 0.6,
                                  bg_pt_size = 0.6,
                                  order = TRUE,
                                  bg_color = "gray95")
  p = patchwork::wrap_plots(p, nrow = 1) +
    patchwork::plot_layout(guides = "collect") +
    ggplot2::theme(legend.position = "right") &
    ggplot2::theme(plot.subtitle = element_blank())
  return(p)
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

Barplot by cluster type :

quantif = table(sobj_ic$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

aquarius::plot_barplot(df = table(sobj_ic$sample_identifier,
                                  sobj_ic$cluster_type) %>%
                         as.data.frame.table() %>%
                         `colnames<-`(c("Sample", "Cell Type", "Number")),
                       x = "Sample", y = "Number", fill = "Cell Type",
                       position = position_stack()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 50 + .data$nb_cells, label = .data$nb_cells),
                      label.size = 0, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers),
                             breaks = names(color_markers),
                             name = "Cell Type") +
  ggplot2::scale_y_continuous(limits = c(0, 100 + max(quantif$nb_cells)),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "none")

Heatmap for macrophages :

subsobj = subset(sobj_ic, cluster_type == "macrophages")
features_oi = c("IL1B", "TNF",
                "HLA-DQA2", "HLA-DPA1", "HLA-DRB5",
                "HLA-A", "HLA-C", "B2M",
                "C1QA", "C1QB", "C1QC")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]  11 463
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Heatmap for CD4 T cells :

subsobj = subset(sobj_ic, cluster_type == "CD4 T cells")
features_oi = c("GZMA", "KLRB1", "BTG1", "ZFP36", "NFKBIA", "TXNIP", "CXCR4", "IFNG", "IL17A")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]   9 848
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "left",
                     annotation_legend_side = "left")

HFSC

Settings

We load the HFSCs dataset :

sobj_hfsc = readRDS(paste0(data_dir, "/4_zoom/2_zoom_hfsc/hfsc_sobj.rds"))
sobj_hfsc
## An object of class Seurat 
## 15384 features across 1454 samples within 1 assay 
## Active assay: RNA (15384 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_24_tsne, RNA_pca_24_umap, harmony, harmony_24_umap, harmony_24_tsne

This is the projection name to visualize cells :

name2D = "harmony_24_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/2_zoom_hfsc/hfsc_list_results.rds"))

lapply(list_results, FUN = names)
## $cluster_0_8
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_2
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_1
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_3
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"

Figures

HFSCs on the full atlas :

sobj$is_hfsc = (colnames(sobj) %in% colnames(sobj_hfsc))

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_hfsc", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[["HFSC"]], bg_color),
                              breaks = c(TRUE, FALSE)) +
  ggplot2::labs(title = "HFSCs",
                subtitle = paste0(ncol(sobj_hfsc), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

KRT15 expression :

Seurat::FeaturePlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                    features = "KRT15") +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() + Seurat::NoLegend()

Genes of interest :

genes = c("TGFB2", "ANGPTL7", "FGF18", "MGP", "EPCAM", "KRT75", "NOTCH3", "PTHLH")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_hfsc$my_gene = Seurat::FetchData(sobj_hfsc, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_hfsc, features = "my_gene", reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_hfsc), replace = FALSE, size = ncol(sobj_hfsc))

# Extract coordinates
cells_coord = sobj_hfsc@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj_hfsc$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

Seurat::DimPlot(sobj_hfsc, reduction = name2D, pt.size = 1,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 7) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Heatmap with proportions :

cluster_markers = c("TGFB2", "ANGPTL7", "EPCAM", "KRT75", "NOTCH3", "PTHLH")

## Bottom annotation : gene expression by cluster
ht_annot = Seurat::FetchData(sobj_hfsc, slot = "data", vars = cluster_markers) %>%
  as.data.frame()
ht_annot$clusters = sobj_hfsc$seurat_clusters
ht_annot = ht_annot %>%
  dplyr::group_by(clusters) %>%
  dplyr::summarise_all(funs('mean' = mean)) %>%
  as.data.frame() %>%
  dplyr::select(-clusters) %>%
  `colnames<-`(c(cluster_markers))

ha_bottom = ComplexHeatmap::HeatmapAnnotation(df = ht_annot,
                                              which = "column",
                                              show_legend = TRUE,
                                              col = setNames(nm = cluster_markers,
                                                             lapply(cluster_markers, FUN = color_fun)),
                                              annotation_name_side = "left")

## Right annotation : number of cells by dataset
ht_annot = table(sobj_hfsc$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  `rownames<-`(.$sample_identifier) %>%
  dplyr::select(-sample_identifier)

ha_right = ComplexHeatmap::HeatmapAnnotation(
  df = ht_annot,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "bottom",
  col = list(nb_cells  = circlize::colorRamp2(colors = RColorBrewer::brewer.pal(name = "Greys", n = 9),
                                              breaks = seq(from = range(ht_annot$nb_cells)[1],
                                                           to = range(ht_annot$nb_cells)[2],
                                                           length.out = 9))))

## Heatmap
ht = aquarius::plot_prop_heatmap(df = sobj_hfsc@meta.data[, c("sample_identifier", "seurat_clusters")],
                                 bottom_annotation = ha_bottom,
                                 # right_annotation = ha_right,
                                 cluster_rows = TRUE,
                                 column_names_centered = TRUE,
                                 prop_margin = 1,
                                 row_names_gp = grid::gpar(names = sample_info$sample_identifier,
                                                           col = sample_info$color,
                                                           fontface = "bold"),
                                 row_title = "Sample",
                                 column_title = "Cluster")

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom")

Heatmap for cluster 0 and 8 :

subsobj = subset(sobj_hfsc, seurat_clusters %in% c(0,8))

features_oi = rownames(list_results$cluster_0_8)
features_oi = features_oi[!grepl(features_oi, pattern = "^RP")]

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = cbind(mat_expr, subsobj$percent.mt)
colnames(mat_expr)[ncol(mat_expr)] = "percent.rb"
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]  48 631
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# list_colors[["seurat_clusters"]] = setNames(aquarius::gg_color_hue(length(levels(subsobj$seurat_clusters))),
#                                             nm = levels(subsobj$seurat_clusters))
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           # clusters = subsobj$seurat_clusters,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]
                                      # clusters = list_colors[["seurat_clusters"]]
                           ))


# g1 : REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM
# g2 : GOBP_APOPTOTIC_PROCESS
g1_genes = c("B2M", "HLA-C", "HLA-A", "MIF", "PPIA", "JUNB", "IFITM3")
g2_genes = c("Jun", "ATF3", "BTG2", "RHOB", "NFKBIA", "SGK1", "KLF9",
             "CAV1", "DDIT4", "PDK4", "TXNIP", "RNF1152", "TLE1")
ha_right = data.frame(genes =  c(features_oi, "percent.rb"), rownames = c(features_oi, "percent.rb"))
ha_right$group = case_when(ha_right$genes %in% g1_genes ~ "REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM",
                           ha_right$genes %in% g2_genes ~ "GOBP_APOPTOTIC_PROCESS",
                           TRUE ~ "others")

list_colors[["group"]] = setNames(
  nm = c("REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM", "GOBP_APOPTOTIC_PROCESS", "others"),
  c("red", "black", "gray90"))

ha_right = HeatmapAnnotation(group = ha_right$group,
                             col = list(group = list_colors[["group"]]),
                             which = "row",
                             show_annotation_name = FALSE,
                             show_legend = TRUE)

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             right_annotation = ha_right,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 10, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Violin plot of IFITM3 :

table(subsobj$sample_type)
## 
##  HS  HD 
## 588  43
ifitm3_hs = subsobj@assays$RNA@data["IFITM3", subsobj$sample_type == "HS"]
ifitm3_hd = subsobj@assays$RNA@data["IFITM3", subsobj$sample_type == "HD"]
ifitm3_hs_VS_ifitm3_hd = stats::t.test(ifitm3_hs, ifitm3_hd)
ifitm3_hs_VS_ifitm3_hd
## 
##  Welch Two Sample t-test
## 
## data:  ifitm3_hs and ifitm3_hd
## t = 7.0204, df = 47.675, p-value = 7.081e-09
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.5509680 0.9933324
## sample estimates:
## mean of x mean of y 
##  1.775647  1.003497
Seurat::VlnPlot(subsobj, group.by = "sample_type",
                features = "IFITM3", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "IFITM3", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of DDIT4 :

table(subsobj$sample_type)
## 
##  HS  HD 
## 588  43
DDIT4_hs = subsobj@assays$RNA@data["DDIT4", subsobj$sample_type == "HS"]
DDIT4_hd = subsobj@assays$RNA@data["DDIT4", subsobj$sample_type == "HD"]
DDIT4_hs_VS_DDIT4_hd = stats::t.test(DDIT4_hs, DDIT4_hd)
DDIT4_hs_VS_DDIT4_hd
## 
##  Welch Two Sample t-test
## 
## data:  DDIT4_hs and DDIT4_hd
## t = -11.338, df = 46.368, p-value = 5.774e-15
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -2.614197 -1.826081
## sample estimates:
## mean of x mean of y 
## 0.9376455 3.1577846
Seurat::VlnPlot(subsobj, group.by = "sample_type",
                features = "DDIT4", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "DDIT4", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

We represent some genes split by sample type :

plot_list = lapply(c("DDIT4", "IFITM3"), FUN = function(one_gene) {
  p = aquarius::plot_split_dimred(sobj_hfsc,
                                  reduction = name2D,
                                  split_by = "sample_type",
                                  color_by = one_gene,
                                  color_palette = c("gray70", "#FDBB84", "#EF6548", "#7F0000", "black"),
                                  main_pt_size = 0.6,
                                  bg_pt_size = 0.6,
                                  order = TRUE,
                                  bg_color = "gray95")
  p = patchwork::wrap_plots(p, nrow = 1) +
    patchwork::plot_layout(guides = "collect") +
    ggplot2::theme(legend.position = "right") &
    ggplot2::theme(plot.subtitle = element_blank())
  return(p)
})

patchwork::wrap_plots(plot_list, ncol = 2)

Barplot with number of HFSCs and total number of cells :

quantif = dplyr::left_join(
  x = table(sobj$sample_identifier) %>%
    as.data.frame.table() %>%
    `colnames<-`(c("Sample", "nb_cells")),
  y = table(sobj_hfsc$sample_identifier) %>%
    as.data.frame.table() %>%
    `colnames<-`(c("Sample", "nb_hfsc")),
  by = "Sample") %>%
  dplyr::mutate(prop_hfsc = round(100*nb_hfsc / nb_cells, 2))

quantif_to_plot = rbind.data.frame(
  data.frame(Sample = quantif$Sample,
             nb_cells = quantif$nb_cells - quantif$nb_hfsc,
             cell_type = "others",
             stringsAsFactors = FALSE),
  data.frame(Sample = quantif$Sample,
             nb_cells = quantif$nb_hfsc,
             cell_type = "hfsc",
             stringsAsFactors = FALSE)) %>%
  dplyr::mutate(cell_type = factor(cell_type, levels = c("others", "hfsc")))

aquarius::plot_barplot(df = quantif_to_plot,
                       x = "Sample", y = "nb_cells", fill = "cell_type",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 0.05+.data$prop_hfsc/100, label = .data$nb_hfsc),
                      label.size = 0, size = 5, fill = NA) +
  ggplot2::scale_fill_manual(values = c("gray90", color_markers[["HFSC"]]),
                             breaks = c("others", "hfsc"),
                             name = "Cell Type") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "none")

IBL and ORS

Settings

We load the IBL + ORS dataset :

sobj_iblors = readRDS(paste0(data_dir, "/4_zoom/3_zoom_iblmors/iblmors_sobj.rds"))
sobj_iblors
## An object of class Seurat 
## 16701 features across 3532 samples within 1 assay 
## Active assay: RNA (16701 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/3_zoom_iblmors/iblmors_list_results.rds"))

lapply(list_results, FUN = names)
## $IBL_vs_ORS
## [1] "mark"        "enrichr_ibl" "enrichr_ors" "gsea"       
## 
## $cluster5_vs_ORS
## [1] "mark"       "enrichr_up" "enrichr_dn" "gsea"      
## 
## $IBL_HS_vs_HD
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $ORS_HS_vs_HD
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"

Preparation

We defined cluster type :

cluster_type = table(sobj_iblors$cell_type, sobj_iblors$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_iblors$cell_type)[cluster_type])

sobj_iblors$cluster_type = cluster_type[sobj_iblors$seurat_clusters]
sobj_iblors$cluster_type = factor(sobj_iblors$cluster_type,
                                  levels = c("IBL", "ORS"))

Figures

IBL + ORS on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj$is_iblors = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                  sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                  by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_iblors", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS")], bg_color),
                              breaks = c("IBL", "ORS", NA), na.value = bg_color) +
  ggplot2::labs(title = "IBL + ORS",
                subtitle = paste0(ncol(sobj_iblors), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_iblors), replace = FALSE, size = ncol(sobj_iblors))

# Extract coordinates
cells_coord = sobj_iblors@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj_iblors$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 8) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type split by sample type :

plot_list = aquarius::plot_split_dimred(sobj_iblors, reduction = name2D,
                                        group_by = "cluster_type",
                                        group_color = color_markers,
                                        split_by = "sample_type",
                                        bg_pt_size = 1, main_pt_size = 1,
                                        bg_color = bg_color)

patchwork::wrap_plots(plot_list) &
  Seurat::NoLegend()

Number of cells by cluster and by sample :

table(sobj_iblors$seurat_clusters,
      sobj_iblors$sample_identifier)
##     
##      HS_1 HS_2 HS_3 HS_4 HS_5 HD_1 HD_2
##   0    49   15   18  354   82   68   83
##   1    14    0   28  205   49   68   74
##   2    67   17  191   19   38   41   29
##   3    40   18    2  204   24   39   57
##   4    14    2   76   18  215    1   36
##   5    84   83    1   69   28   20   17
##   6    40    9  108   23   76   26   20
##   7    27    7   51   90   21   10   23
##   8    28    7   77   11   20   28   13
##   9    11    4   41   11   73   10    8
##   10   16    3   37    6   26   10    4
table(sobj_iblors$cluster_type,
      sobj_iblors$sample_identifier)
##      
##       HS_1 HS_2 HS_3 HS_4 HS_5 HD_1 HD_2
##   IBL  176   42  530   88  448  116  110
##   ORS  214  123  100  922  204  205  254

Separate cluster 5 :

sobj_iblors$cluster_type_sep5 = ifelse(sobj_iblors$seurat_clusters == 5,
                                       yes = "ORS_5",
                                       no = as.character(sobj_iblors$cluster_type)) %>%
  as.factor()

table(sobj_iblors$cluster_type_sep5,
      sobj_iblors$sample_type)
##        
##           HS   HD
##   IBL   1284  226
##   ORS   1298  422
##   ORS_5  265   37

Barplot by cluster family :

quantif = table(sobj_iblors$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

quantif_to_plot = table(sobj_iblors$sample_identifier,
                        sobj_iblors$cluster_type_sep5) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "CellType", "Number")) %>%
  dplyr::mutate(Style = ifelse(CellType == "ORS_5", yes = "IL1R2+", no = "IL1R2-")) %>%
  dplyr::mutate(Style = factor(Style, levels = c("IL1R2-", "IL1R2+"))) %>%
  dplyr::mutate(CellType = ifelse(CellType == "ORS_5", yes = "ORS", no = as.character(CellType))) %>%
  `colnames<-`(c("Sample", "Cell Type", "Number", "IL1R2 status"))

aquarius::plot_barplot(df = quantif_to_plot,
                       x = "Sample", y = "Number",
                       fill = "Cell Type", pattern = "IL1R2 status",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 1.05, label = .data$nb_cells),
                      label.size = 0, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers[levels(sobj_iblors$cluster_type)]),
                             breaks = names(color_markers[levels(sobj_iblors$cluster_type)]),
                             name = "Cell Type") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "right")

Focus on cluster 5, among ORS :

subsobj = subset(sobj_iblors, cluster_type == "ORS")
subsobj$cluster_type_sep5 = ifelse(subsobj$seurat_clusters == 5,
                                   yes = "ORS_5",
                                   no = as.character(subsobj$cluster_type)) %>%
  as.factor()

quantif = table(subsobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

quantif_to_plot = table(subsobj$sample_identifier,
                        subsobj$cluster_type_sep5) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "CellType", "Number")) %>%
  dplyr::mutate(Style = ifelse(CellType == "ORS_5", yes = "IL1R2+", no = "IL1R2-")) %>%
  dplyr::mutate(Style = factor(Style, levels = c("IL1R2-", "IL1R2+"))) %>%
  dplyr::mutate(CellType = ifelse(CellType == "ORS_5", yes = "ORS", no = as.character(CellType))) %>%
  `colnames<-`(c("Sample", "Cell Type", "Number", "IL1R2 status")) %>%
  dplyr::left_join(., y = quantif, by = "Sample") %>%
  dplyr::mutate(prop_cluster5 = round(Number/nb_cells, 4))

aquarius::plot_barplot(df = quantif_to_plot,
                       x = "Sample", y = "Number",
                       fill = "IL1R2 status",
                       position = position_fill()) +
  # ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
  #                     aes(x = .data$Sample, y = 1.05, label = .data$nb_cells),
  #                     label.size = 0, size = 5) +
  ggplot2::geom_label(data = quantif_to_plot %>%
                        dplyr::filter(`IL1R2 status` == "IL1R2+"),
                      inherit.aes = FALSE,
                      aes(x = .data$Sample, y = prop_cluster5 + 0.05, label = .data$Number),
                      label.size = 0, size = 5, fill = NA, col = "white") +
  ggplot2::scale_fill_manual(values = c("black", color_markers[["ORS"]]),
                             breaks = c("IL1R2+", "IL1R2-"),
                             name = "IL1R2 status") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "right")

DE genes between IBL and ORS :

mark = list_results$IBL_vs_ORS$mark
mark$gene_name = rownames(mark)
mark_label = rbind(
  # up-regulated in IBL
  mark %>% dplyr::top_n(., n = 20, wt = avg_logFC),
  # up-regulated in ORS
  mark %>% dplyr::top_n(., n = 20, wt = -avg_logFC),
  # representative and selective for IBL
  mark %>% dplyr::top_n(., n = 20, wt = (pct.1 - pct.2)),
  # representative and selective for ORS
  mark %>% dplyr::top_n(., n = 20, wt = -(pct.1 - pct.2))) %>%
  dplyr::distinct()
mark_label = mark_label[!grepl(rownames(mark_label), pattern = "^MT"), ]

avg_logFC_range = setNames(c(min(mark_label$avg_logFC), -1, 0, 1, max(mark_label$avg_logFC)),
                           nm = c("dodgerblue4", "dodgerblue3", "#B7B7B7", "firebrick3", "firebrick4"))


ggplot2::ggplot(mark, aes(x = pct.1, y = pct.2, col = avg_logFC)) +
  ggplot2::geom_abline(slope = 1, intercept = 0, lty = 2) +
  ggplot2::geom_point() +
  ggrepel::geom_label_repel(data = mark_label, max.overlaps = Inf,
                            aes(x = pct.1, y = pct.2, label = gene_name),
                            col = "black", fill = NA, size = 3.5, label.size = NA) +
  ggplot2::labs(x = "Enriched in IBL",
                y = "Enriched in ORS") +
  ggplot2::scale_color_gradientn(colors = names(avg_logFC_range),
                                 values = scales::rescale(unname(avg_logFC_range))) +
  ggplot2::theme_classic() +
  ggplot2::theme(aspect.ratio = 1)

GSEA plot :

the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      " | pvalue : ", round(the_content$pvalue, 4),
                      " | set size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      " | pvalue : ", round(the_content$pvalue, 4),
                      " | set size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

Score for both gene sets, in all cells :

gene_sets = aquarius::get_gene_sets(species = "Homo sapiens")
the_gs_name = "REACTOME_KERATINIZATION" 
the_gs_content = gene_sets$gene_sets_full %>%
  dplyr::filter(gs_name == the_gs_name) %>%
  dplyr::pull(gene_symbol) %>%
  unlist()

sobj_iblors$score_kera = Seurat::AddModuleScore(sobj_iblors,
                                                features = list(the_gs_content))$Cluster1

Seurat::VlnPlot(sobj_iblors, features = "score_kera", pt.size = 0.05,
                split.by = "sample_type", group.by = "cluster_type",
                cols = rev(sample_type_colors)) +
  ggplot2::labs(title = the_gs_name) +
  ggplot2::theme(axis.title.x = element_blank())

the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_gs_content = gene_sets$gene_sets_full %>%
  dplyr::filter(gs_name == the_gs_name) %>%
  dplyr::pull(gene_symbol) %>%
  unlist()

sobj_iblors$score_ifna = Seurat::AddModuleScore(sobj_iblors,
                                                features = list(the_gs_content))$Cluster1

Seurat::VlnPlot(sobj_iblors, features = "score_ifna", pt.size = 0.05,
                split.by = "sample_type", group.by = "cluster_type",
                cols = rev(sample_type_colors)) +
  ggplot2::labs(title = the_gs_name) +
  ggplot2::theme(axis.title.x = element_blank())

Violin plot for IBL :

subsobj = subset(sobj_iblors, cluster_type == "IBL")

Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.05,
                features = c("DUSP1", "DDIT4", "MIF", "LGALS7", "ARF5", "S100A9"),
                cols = sample_type_colors, ncol = 6) &
  ggplot2::theme(axis.title.x = element_blank(),
                 axis.title.y = element_blank(),
                 legend.position = "none")

Violin plot for ORS :

subsobj = subset(sobj_iblors, cluster_type == "ORS")

Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.05,
                features = c("DUSP1", "KLF6", "CLDN1", "CTGF",
                             "S100A9", "CCL2", "IFITM3", "IFI27"),
                cols = sample_type_colors, ncol = 8) &
  ggplot2::theme(axis.title.x = element_blank(),
                 axis.title.y = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "IFI27", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Heatmap for cluster 5 vs other ORS :

subsobj = subset(sobj_iblors, cluster_type == "ORS")

features_oi = c("YBX3", "TXNIP", "KRT14", "KRT15", "NEAT1",
                "FXYD3", "MT2A", "MT1E", "MT1X", "AQP3", "GLUL",
                # "HALLMARK_TNFA_SIGNALING_VIA_NFKB"
                "FOS", "JUNB", "DUSP1", "ZFP36", "NFKBIZ",
                "ATF3", "RHOB",  "ETS2", "IL18", "KLF4", "KLF6", "KLF9",
                "KLF3", "KLF5", "COL17A1", "THSD4", "WNT3", "WNT4", "SLPI", "PLAT",
                "LAMB4", "DCN", "SPINK5",
                "GSTM3", "ALDH3A1",  "LGALS7B", "SLC38A2", "EHF",  "CLEC2B",
                "IL20RB", "IL1R2", "IFI27", "CXCL14", "HLA-C", "GPSM2", "DAAM1",   "ID1",
                "RNASET2", "HOPX", "POU3F1", "SPRY1", "AR", "PDGFC",
                "WFDC2", "WFDC5", "TSC22D3", "FGFR3",  "LY6D", "IGFBP3", 
                # Other ORS
                "APOE", "CTSB", "CALD1", "SOX4",
                "STMN1", "LMO4", "CEBPB", "TMEM45A", "GPX2", "C1QTNF12", "GJB6",
                "KRT6A", "KRT17", "RBP1", "CALML3", "PTN", "DAPK2",
                "EGLN3", "FILIP1L", "ADGRL3", "FST", "EFNB2", "SEMA5A",
                "FGFR1", "EGR2", "CLDN1", "DEFB1", "CARD18", "MGST1")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]   89 2022
## Colors
list_colors = list()
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
list_colors[["population"]] = setNames(nm = c("IL1R2+ ORS", "other ORS"),
                                       c("black", color_markers["ORS"]))
list_colors[["nFeature_RNA"]] = circlize::colorRamp2(breaks = seq(from = min(subsobj$nFeature_RNA),
                                                                  to = max(subsobj$nFeature_RNA),
                                                                  length.out = 9),
                                                     colors = RColorBrewer::brewer.pal(name = "Greys", n = 9))

# Cells order
column_order = subsobj@meta.data %>%
  dplyr::mutate(seurat_clusters = factor(seurat_clusters, levels = c(5, 3, 0, 1, 7))) %>%
  dplyr::arrange(sample_type, seurat_clusters, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Annotation
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           population = ifelse(subsobj$cluster_type_sep5 == "ORS",
                                               yes = "other ORS", no = "IL1R2+ ORS"),
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]],
                                      population = list_colors[["population"]]))

ha_bottom = HeatmapAnnotation(nFeature_RNA = subsobj$nFeature_RNA,
                              col = list(nFeature_RNA = list_colors[["nFeature_RNA"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             bottom_annotation = ha_bottom,
             # Cell grouping
             column_split = subsobj$sample_type %>% as.character(),
             cluster_columns = FALSE,
             column_order = column_order,
             column_title = NULL,
             show_column_dend = FALSE,
             show_column_names = FALSE,
             # Genes
             cluster_rows = FALSE,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             # Style
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "right",
                     annotation_legend_side = "right")

Genes of interest :

genes = c("KRT16", "COL17A1", "DST", "KRT6B", "IL1R2", "WNT3",
          "IFI27", "CXCL14", "IGFBP3", "KRT15", "CD200", "IL18")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_iblors$my_gene = Seurat::FetchData(sobj_iblors, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_iblors, features = "my_gene", reduction = name2D, pt.size = 0.25) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

## 
## [[9]]

## 
## [[10]]

## 
## [[11]]

## 
## [[12]]

HFSCs to IBL and ORS

Settings

We load the merged dataset :

sobj_traj = readRDS(paste0(data_dir, "/4_zoom/4_zoom_hfsc_iblmors/hfsc_iblmors_sobj_traj_tinga.rds"))
sobj_traj
## An object of class Seurat 
## 17050 features across 4986 samples within 1 assay 
## Active assay: RNA (17050 features, 2000 variable features)
##  8 dimensional reductions calculated: RNA_pca, RNA_pca_18_tsne, RNA_pca_18_umap, harmony, harmony_18_umap, harmony_18_tsne, harmony_dm, harmony_dm_5_umap

This is the projection name to visualize cells :

name2D = "harmony_dm"

We load the trajectory object for visualisation purpose :

my_traj = readRDS(paste0(data_dir, "/4_zoom/4_zoom_hfsc_iblmors/hfsc_iblmors_my_traj_tinga.rds"))
class(my_traj)
## [1] "dynwrap::with_dimred"     "dynwrap::with_trajectory"
## [3] "dynwrap::data_wrapper"    "list"

Preparation

We defined cell type based on individual object :

sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj_traj$cell_bc = colnames(sobj_traj)
sobj_traj$cluster_type = dplyr::left_join(sobj_traj@meta.data[, c("cell_bc", "percent.mt")],
                                          sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                          by = "cell_bc")[, "cluster_type"] %>% as.character()
sobj_traj$cluster_type = ifelse(colnames(sobj_traj) %in% colnames(sobj_hfsc),
                                yes = "HFSC",
                                no = sobj_traj$cluster_type) %>%
  as.factor()

Figures

Cells on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_traj$cell_bc = colnames(sobj_traj)
sobj$is_traj = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                sobj_traj@meta.data[, c("cell_bc", "cluster_type")],
                                by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_traj", order = levels(sobj_traj$cluster_type)) +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS", "HFSC")], bg_color),
                              breaks = c("IBL", "ORS", "HFSC", NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() + Seurat::NoLegend()

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_traj), replace = FALSE, size = ncol(sobj_traj))

# Extract coordinates
cells_coord = sobj_traj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_type = sobj_traj$sample_type
cells_coord = cells_coord[order(sobj_traj$sample_type), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_type)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_type_colors,
                              breaks = names(sample_type_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster type :

Seurat::DimPlot(sobj_traj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Pseudotime :

Seurat::FeaturePlot(sobj_traj, reduction = name2D, pt.size = 0.5,
                    features = "pseudotime") +
  ggplot2::scale_color_gradientn(colors = viridis::viridis(n = 100)) +
  ggplot2::lims(x = range(sobj_traj@reductions[[name2D]]@cell.embeddings[, 1]),
                y = range(sobj_traj@reductions[[name2D]]@cell.embeddings[, 2])) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes()

Pseudotime with dynplot’s function :

dynplot::plot_dimred(trajectory = my_traj,
                     dimred = sobj_traj[[name2D]]@cell.embeddings,
                     # Cells
                     color_cells = 'pseudotime',
                     size_cells = 1.6,
                     border_radius_percentage = 0,
                     # Trajectory
                     plot_trajectory = TRUE,
                     color_trajectory = "none",
                     label_milestones = FALSE,
                     size_milestones = 0,
                     size_transitions = 1)

OEP002321 dataset

Settings

We load the dataset containing all cells :

sobj = readRDS(paste0(data_dir, "/5_wu/3_combined/wu_sobj.rds"))
sobj
## An object of class Seurat 
## 17727 features across 17762 samples within 1 assay 
## Active assay: RNA (17727 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

This is the projection name to visualize cells :

name2D = "harmony_38_tsne"
name2D_atlas = name2D

These are all the samples analyzed :

sample_info = readRDS(paste0(data_dir, "/5_wu/1_metadata/wu_sample_info.rds"))

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

We define cluster type and cluster family :

sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type)) %>%
  base::droplevels()
sobj$cluster_family = custom_order_cell_type[sobj$cluster_type, "cell_family"]
sobj$cluster_family = factor(sobj$cluster_family,
                             levels = names(family_color))

Global figures

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D_atlas]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cell type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Gene expression to assess annotation :

genes = c("PTPRC", "MSX2", "KRT14")
names(genes) = c("immune cells", "matrix cells", "non-matrix cells")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  pop = names(genes)[gene_id]
  
  sobj$my_gene = Seurat::FetchData(sobj, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj, features = "my_gene", reduction = name2D_atlas) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) + 
    # subtitle = pop) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

Dotplot :

custom_order_cell_type = custom_order_cell_type[levels(sobj$cluster_type), c("cell_type", "cell_family")]

plot_list = Seurat::DotPlot(sobj,
                            features = c("PTPRC",
                                         "CD3E", "CD4",
                                         "CD207", "AIF1",
                                         # "PRDM1", "KRT85",
                                         "MSX2",
                                         "KRT32", "KRT35",
                                         "KRT31", "PRR9",
                                         "BAMBI", "ALDH1A3",
                                         "KRT71", "KRT73",
                                         "TOP2A", "MCM5",
                                         "KRT14", "CXCL14",
                                         "KRT15", "COL17A1",
                                         "DIO2", "TCEAL2",
                                         "KRT16", "KRT6C",
                                         "SPINK5", "LY6D"),
                            group.by = "cluster_type", scale = TRUE,
                            scale.by = "radius", scale.min = NA, scale.max = NA) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "bottom",
                 legend.direction = "vertical",
                 # legend.justification = "bottom",
                 legend.box = "horizontal",
                 legend.box.margin = margin(0,25,0,0),
                 axis.title = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.text.y = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 11, color = "black"),
                 plot.margin = unit(c(0,0.5,0,0), "cm"))

p = ggplot2::ggplot(custom_order_cell_type, aes(y = cell_type, x = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(y = 0.5, yend = 2.5, x = 0, xend = 0), size = 6, col = family_color["immune cells"]) +
  ggplot2::geom_segment(aes(y = 2.5, yend = 7.5, x = 0, xend = 0), size = 6, col = family_color["matrix"]) +
  ggplot2::geom_segment(aes(y = 7.5, yend = 11.5, x = 0, xend = 0), size = 6, col = family_color["non matrix"]) +
  ggplot2::scale_x_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.x = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.title = element_blank(),
                 axis.line.x = element_blank(),
                 axis.text.y = element_text(size = 12, color = "black"),
                 plot.margin = unit(c(0.5,0,0,0.5), "cm"))

plot_list = patchwork::wrap_plots(p, plot_list,
                                  nrow = 1, widths = c(1, 25))
plot_list

IBL and ORS dataset

We load the IBL + ORS dataset :

sobj_iblors = readRDS(paste0(data_dir, "/5_wu/4_ibl_ors/iblmors_sobj.rds"))
sobj_iblors
## An object of class Seurat 
## 15541 features across 8026 samples within 1 assay 
## Active assay: RNA (15541 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/5_wu/4_ibl_ors/iblmors_list_results.rds"))

lapply(list_results, FUN = names)
## $IBL_vs_ORS
## [1] "mark"        "enrichr_ibl" "enrichr_ors" "gsea"

We defined cluster type :

cluster_type = table(sobj_iblors$cell_type, sobj_iblors$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_iblors$cell_type)[cluster_type])

sobj_iblors$cluster_type = cluster_type[sobj_iblors$seurat_clusters]
sobj_iblors$cluster_type = factor(sobj_iblors$cluster_type,
                                  levels = c("IBL", "ORS"))

IBL + ORS figure

IBL + ORS on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj$is_iblors = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                  sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                  by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_iblors", order = FALSE) +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS")], bg_color),
                              breaks = c("IBL", "ORS", NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes() + Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

DE genes between IBL and ORS :

mark = list_results$IBL_vs_ORS$mark
mark$gene_name = rownames(mark)
mark_label = rbind(
  # up-regulated in IBL
  mark %>% dplyr::top_n(., n = 20, wt = avg_logFC),
  # up-regulated in ORS
  mark %>% dplyr::top_n(., n = 20, wt = -avg_logFC),
  # representative and selective for IBL
  mark %>% dplyr::top_n(., n = 20, wt = (pct.1 - pct.2)),
  # representative and selective for ORS
  mark %>% dplyr::top_n(., n = 20, wt = -(pct.1 - pct.2))) %>%
  dplyr::distinct()
mark_label = mark_label[!grepl(rownames(mark_label), pattern = "^MT"), ]

avg_logFC_range = setNames(c(min(mark_label$avg_logFC), -1, 0, 1, max(mark_label$avg_logFC)),
                           nm = c("dodgerblue4", "dodgerblue3", "#B7B7B7", "firebrick3", "firebrick4"))


ggplot2::ggplot(mark, aes(x = pct.1, y = pct.2, col = avg_logFC)) +
  ggplot2::geom_abline(slope = 1, intercept = 0, lty = 2) +
  ggplot2::geom_point() +
  ggrepel::geom_label_repel(data = mark_label, max.overlaps = Inf,
                            aes(x = pct.1, y = pct.2, label = gene_name),
                            col = "black", fill = NA, size = 3.5, label.size = NA) +
  ggplot2::labs(x = "Enriched in IBL",
                y = "Enriched in ORS") +
  ggplot2::scale_color_gradientn(colors = names(avg_logFC_range),
                                 values = scales::rescale(unname(avg_logFC_range))) +
  ggplot2::theme_classic() +
  ggplot2::theme(aspect.ratio = 1)

GSEA plot :

the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      " | pvalue : ", round(the_content$pvalue, 4),
                      " | set size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      " | pvalue : ", round(the_content$pvalue, 4),
                      " | set size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

Genes of interest :

genes = c("KRT16", "COL17A1", "DST", "KRT6B", "IL1R2", "WNT3",
          "IFI27", "CXCL14", "IGFBP3", "KRT15", "CD200")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_iblors$my_gene = Seurat::FetchData(sobj_iblors, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_iblors, features = "my_gene", reduction = name2D, pt.size = 0.25) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

## 
## [[9]]

## 
## [[10]]

## 
## [[11]]

Supplementary tables

In this section, we save files to associate with the manuscript, as supplementary tables.

Table S2 (package version)

We load the table :

package_version = read.table(paste0(".", "/data/info_to_install_2023_04_17.txt"),
                             header = TRUE)
head(package_version)
##   order package_name  version
## 1     1      acepack    1.4.1
## 2     2    ADGofTest      0.3
## 3     3    backports    1.2.1
## 4     4    base64enc    0.1-3
## 5     5           BH 1.72.0-3
## 6     6          bit    4.0.4
##                                                                              url
## 1                    https://cran.r-project.org/src/contrib/acepack_1.4.1.tar.gz
## 2                    https://cran.r-project.org/src/contrib/ADGofTest_0.3.tar.gz
## 3 http://cran.r-project.org/src/contrib/Archive/backports/backports_1.2.1.tar.gz
## 4                  https://cran.r-project.org/src/contrib/base64enc_0.1-3.tar.gz
## 5            http://cran.r-project.org/src/contrib/Archive/BH/BH_1.72.0-3.tar.gz
## 6             http://cran.r-project.org/src/contrib/Archive/bit/bit_4.0.4.tar.gz
##           type
## 1    CRAN_last
## 2    CRAN_last
## 3 CRAN_archive
## 4    CRAN_last
## 5 CRAN_archive
## 6 CRAN_archive

Columns correspond to :

  • order : order to install package such that one never need to install dependencies
  • package_name : package name
  • version : installed version, on the local machine
  • url : the package was installed in the Singularity container using this url

We save this table, except the last column (just for me) :

openxlsx::write.xlsx(package_version[, c(1:4)],
                     file = paste0(".", "/data/Supplementary Table 2.xlsx"))

Table S3 (cell type annotation)

We load the table :

cell_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_cell_markers.rds"))
lengths(cell_markers)
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##               13               13                9               10 
##          B cells          cuticle           cortex          medulla 
##               16               15               16               10 
##              IRS    proliferative              IBL              ORS 
##               16               20               15               16 
##              IFE             HFSC      melanocytes        sebocytes 
##               17               17               10                8

We save this table, except the last column (just for me) :

openxlsx::write.xlsx(cell_markers,
                     file = paste0(".", "/data/Supplementary Table 3.xlsx"))

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
##  [1] parallel  stats4    grid      stats     graphics  grDevices utils    
##  [8] datasets  methods   base     
## 
## other attached packages:
##  [1] org.Mm.eg.db_3.10.0   AnnotationDbi_1.48.0  IRanges_2.20.2       
##  [4] S4Vectors_0.24.4      Biobase_2.46.0        BiocGenerics_0.32.0  
##  [7] ComplexHeatmap_2.14.0 ggplot2_3.3.5         patchwork_1.1.2      
## [10] dplyr_1.0.7          
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 dyndimred_1.0.3            
##   [7] vctrs_0.3.8                 usethis_2.0.1              
##   [9] dynwrap_1.2.1               blob_1.2.1                 
##  [11] survival_3.2-13             prodlim_2019.11.13         
##  [13] dynutils_1.0.5              later_1.3.0                
##  [15] DBI_1.1.1                   R.utils_2.11.0             
##  [17] SingleCellExperiment_1.8.0  rappdirs_0.3.3             
##  [19] uwot_0.1.8                  dqrng_0.2.1                
##  [21] jpeg_0.1-8.1                zlibbioc_1.32.0            
##  [23] pspline_1.0-18              pcaMethods_1.78.0          
##  [25] mvtnorm_1.1-1               htmlwidgets_1.5.4          
##  [27] GlobalOptions_0.1.2         future_1.22.1              
##  [29] UpSetR_1.4.0                laeken_0.5.2               
##  [31] leiden_0.3.3                clustree_0.4.3             
##  [33] lmds_0.1.0                  scater_1.14.6              
##  [35] irlba_2.3.3                 markdown_1.1               
##  [37] DEoptimR_1.0-9              tidygraph_1.1.2            
##  [39] Rcpp_1.0.9                  readr_2.0.2                
##  [41] KernSmooth_2.23-17          carrier_0.1.0              
##  [43] promises_1.1.0              gdata_2.18.0               
##  [45] DelayedArray_0.12.3         limma_3.42.2               
##  [47] graph_1.64.0                RcppParallel_5.1.4         
##  [49] Hmisc_4.4-0                 fs_1.5.2                   
##  [51] RSpectra_0.16-0             fastmatch_1.1-0            
##  [53] ranger_0.12.1               digest_0.6.25              
##  [55] png_0.1-7                   sctransform_0.2.1          
##  [57] cowplot_1.0.0               DOSE_3.12.0                
##  [59] here_1.0.1                  TInGa_0.0.0.9000           
##  [61] dynplot_1.1.0               ggraph_2.0.3               
##  [63] pkgconfig_2.0.3             GO.db_3.10.0               
##  [65] DelayedMatrixStats_1.8.0    gower_0.2.1                
##  [67] ggbeeswarm_0.6.0            iterators_1.0.12           
##  [69] DropletUtils_1.6.1          reticulate_1.26            
##  [71] clusterProfiler_3.14.3      SummarizedExperiment_1.16.1
##  [73] circlize_0.4.15             beeswarm_0.4.0             
##  [75] GetoptLong_1.0.5            xfun_0.35                  
##  [77] bslib_0.3.1                 zoo_1.8-10                 
##  [79] tidyselect_1.1.0            GA_3.2                     
##  [81] reshape2_1.4.4              purrr_0.3.4                
##  [83] ica_1.0-2                   pcaPP_1.9-73               
##  [85] viridisLite_0.3.0           rtracklayer_1.46.0         
##  [87] rlang_1.0.2                 hexbin_1.28.1              
##  [89] jquerylib_0.1.4             dyneval_0.9.9              
##  [91] glue_1.4.2                  waldo_0.3.1                
##  [93] RColorBrewer_1.1-2          matrixStats_0.56.0         
##  [95] stringr_1.4.0               lava_1.6.7                 
##  [97] europepmc_0.3               DESeq2_1.26.0              
##  [99] recipes_0.1.17              labeling_0.3               
## [101] httpuv_1.5.2                class_7.3-17               
## [103] BiocNeighbors_1.4.2         DO.db_2.9                  
## [105] annotate_1.64.0             jsonlite_1.7.2             
## [107] XVector_0.26.0              bit_4.0.4                  
## [109] mime_0.9                    aquarius_0.1.5             
## [111] Rsamtools_2.2.3             gridExtra_2.3              
## [113] gplots_3.0.3                stringi_1.4.6              
## [115] processx_3.5.2              gsl_2.1-6                  
## [117] bitops_1.0-6                cli_3.0.1                  
## [119] batchelor_1.2.4             RSQLite_2.2.0              
## [121] randomForest_4.6-14         tidyr_1.1.4                
## [123] data.table_1.14.2           rstudioapi_0.13            
## [125] units_0.7-2                 GenomicAlignments_1.22.1   
## [127] nlme_3.1-147                qvalue_2.18.0              
## [129] scran_1.14.6                locfit_1.5-9.4             
## [131] scDblFinder_1.1.8           listenv_0.8.0              
## [133] ggthemes_4.2.4              gridGraphics_0.5-0         
## [135] R.oo_1.24.0                 dbplyr_1.4.4               
## [137] TTR_0.24.2                  readxl_1.3.1               
## [139] lifecycle_1.0.1             timeDate_3043.102          
## [141] ggpattern_0.3.1             munsell_0.5.0              
## [143] cellranger_1.1.0            R.methodsS3_1.8.1          
## [145] proxyC_0.1.5                visNetwork_2.0.9           
## [147] caTools_1.18.0              codetools_0.2-16           
## [149] GenomeInfoDb_1.22.1         vipor_0.4.5                
## [151] lmtest_0.9-38               msigdbr_7.5.1              
## [153] htmlTable_1.13.3            triebeard_0.3.0            
## [155] lsei_1.2-0                  xtable_1.8-4               
## [157] ROCR_1.0-7                  classInt_0.4-3             
## [159] BiocManager_1.30.10         scatterplot3d_0.3-41       
## [161] abind_1.4-5                 farver_2.0.3               
## [163] parallelly_1.28.1           RANN_2.6.1                 
## [165] askpass_1.1                 GenomicRanges_1.38.0       
## [167] RcppAnnoy_0.0.16            tibble_3.1.5               
## [169] ggdendro_0.1-20             cluster_2.1.0              
## [171] future.apply_1.5.0          Seurat_3.1.5               
## [173] dendextend_1.15.1           Matrix_1.3-2               
## [175] ellipsis_0.3.2              prettyunits_1.1.1          
## [177] lubridate_1.7.9             ggridges_0.5.2             
## [179] igraph_1.2.5                RcppEigen_0.3.3.7.0        
## [181] fgsea_1.12.0                remotes_2.4.2              
## [183] scBFA_1.0.0                 destiny_3.0.1              
## [185] VIM_6.1.1                   testthat_3.1.0             
## [187] htmltools_0.5.2             BiocFileCache_1.10.2       
## [189] yaml_2.2.1                  utf8_1.1.4                 
## [191] plotly_4.9.2.1              XML_3.99-0.3               
## [193] ModelMetrics_1.2.2.2        e1071_1.7-3                
## [195] foreign_0.8-76              withr_2.5.0                
## [197] fitdistrplus_1.0-14         BiocParallel_1.20.1        
## [199] xgboost_1.4.1.1             bit64_4.0.5                
## [201] foreach_1.5.0               robustbase_0.93-9          
## [203] Biostrings_2.54.0           GOSemSim_2.13.1            
## [205] rsvd_1.0.3                  memoise_2.0.0              
## [207] evaluate_0.18               forcats_0.5.0              
## [209] rio_0.5.16                  geneplotter_1.64.0         
## [211] tzdb_0.1.2                  caret_6.0-86               
## [213] ps_1.6.0                    DiagrammeR_1.0.6.1         
## [215] curl_4.3                    fdrtool_1.2.15             
## [217] fansi_0.4.1                 highr_0.8                  
## [219] urltools_1.7.3              xts_0.12.1                 
## [221] GSEABase_1.48.0             acepack_1.4.1              
## [223] edgeR_3.28.1                checkmate_2.0.0            
## [225] scds_1.2.0                  cachem_1.0.6               
## [227] npsurv_0.4-0                babelgene_22.3             
## [229] rjson_0.2.20                openxlsx_4.1.5             
## [231] ggrepel_0.9.1               clue_0.3-60                
## [233] rprojroot_2.0.2             stabledist_0.7-1           
## [235] tools_3.6.3                 sass_0.4.0                 
## [237] nichenetr_1.1.1             magrittr_2.0.1             
## [239] RCurl_1.98-1.2              proxy_0.4-24               
## [241] car_3.0-11                  ape_5.3                    
## [243] ggplotify_0.0.5             xml2_1.3.2                 
## [245] httr_1.4.2                  assertthat_0.2.1           
## [247] rmarkdown_2.18              boot_1.3-25                
## [249] globals_0.14.0              R6_2.4.1                   
## [251] Rhdf5lib_1.8.0              nnet_7.3-14                
## [253] RcppHNSW_0.2.0              progress_1.2.2             
## [255] genefilter_1.68.0           statmod_1.4.34             
## [257] gtools_3.8.2                shape_1.4.6                
## [259] sf_1.0-3                    HDF5Array_1.14.4           
## [261] BiocSingular_1.2.2          rhdf5_2.30.1               
## [263] splines_3.6.3               AUCell_1.8.0               
## [265] carData_3.0-4               colorspace_1.4-1           
## [267] generics_0.1.0              base64enc_0.1-3            
## [269] dynfeature_1.0.0            smoother_1.1               
## [271] gridtext_0.1.1              pillar_1.6.3               
## [273] tweenr_1.0.1                sp_1.4-1                   
## [275] ggplot.multistats_1.0.0     rvcheck_0.1.8              
## [277] GenomeInfoDbData_1.2.2      plyr_1.8.6                 
## [279] gtable_0.3.0                zip_2.2.0                  
## [281] knitr_1.41                  latticeExtra_0.6-29        
## [283] biomaRt_2.42.1              fastmap_1.1.0              
## [285] ADGofTest_0.3               copula_1.0-0               
## [287] doParallel_1.0.15           vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] ipred_0.9-12                enrichplot_1.6.1           
## [295] hms_1.1.1                   ggforce_0.3.1              
## [297] Rtsne_0.15                  shiny_1.7.1                
## [299] gridpattern_0.3.1           numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             lazyeval_0.2.2             
## [303] Formula_1.2-3               tsne_0.1-3                 
## [305] crayon_1.3.4                MASS_7.3-54                
## [307] pROC_1.16.2                 viridis_0.5.1              
## [309] dynparam_1.0.0              rpart_4.1-15               
## [311] zinbwave_1.8.0              compiler_3.6.3             
## [313] ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJGaWd1cmVzIgphdXRob3I6ICJBdWRyZXkiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVZLSVtLSVkJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKLS0tCgo8c3R5bGU+CmJvZHkgewp0ZXh0LWFsaWduOiBqdXN0aWZ5fQo8L3N0eWxlPgoKPCEtLSBBdXRvbWF0aWNhbGx5IGNvbXB1dGVzIGFuZCBwcmludHMgaW4gdGhlIG91dHB1dCB0aGUgcnVubmluZyB0aW1lIGZvciBhbnkgY29kZSBjaHVuayAtLT4KYGBge3IsIGVjaG89RkFMU0V9CiMgaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcm1hcmtkb3duL2lzc3Vlcy8xNDUzCmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKICAgIAogICAgaWYgKGlzRkFMU0Uob3B0aW9uc1tbcGFzdGUwKCJmb2xkXyIsIHR5cGUpXV0pKSByZXR1cm4ocmVzKQogICAgCiAgICBwYXN0ZTAoCiAgICAgICI8ZGV0YWlscz48c3VtbWFyeT4iLCAic2hvdyIsICI8L3N1bW1hcnk+XG5cbiIsCiAgICAgIHJlcywKICAgICAgIlxuXG48L2RldGFpbHM+IgogICAgKQogIH0KfQprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgb3V0cHV0ID0gaG9va19mb2xkYWJsZSgib3V0cHV0IiksCiAgcGxvdCA9IGhvb2tfZm9sZGFibGUoInBsb3QiKSwKICB0aW1lX2l0ID0gbG9jYWwoewogICAgbm93ID0gTlVMTAogICAgZnVuY3Rpb24oYmVmb3JlLCBvcHRpb25zKSB7CiAgICAgIGlmIChvcHRpb25zJHRpbWVfaXQpIHsKICAgICAgICBpZiAoYmVmb3JlKSB7CiAgICAgICAgICBub3cgPD0gU3lzLnRpbWUoKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXMgPSBkaWZmdGltZShTeXMudGltZSgpLCBub3csIHVuaXRzID0gInNlY3MiKQogICAgICAgICAgcGFzdGUoIihUaW1lIHRvIHJ1biA6Iiwgcm91bmQocmVzLCBkaWdpdHMgPSAyKSwgInMpIikKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9KQopCmBgYAoKPCEtLSBTZXQgZGVmYXVsdCBwYXJhbWV0ZXJzIGZvciBhbGwgY2h1bmtzIC0tPgpgYGB7ciwgc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc2V0LnNlZWQoMTMzN0wpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgIyBkaXNwbGF5IGNvZGUKICAgICAgICAgICAgICAgICAgICAgICMgZGlzcGxheSBjaHVuayBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfb3V0cHV0ID0gRkFMU0UsICMgdXNlZnVsIGZvciBzZXNzaW9uSW5mbygpCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX3Bsb3QgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBmaWd1cmUgc2V0dGluZ3MKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgc29tZXRoaW5nIGFib3V0IHNlZWQsIGNodW5rIGFuZCBSbWFya2Rvd24gY29tcGlsYXRpb24KICAgICAgICAgICAgICAgICAgICAgICMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzk0MTcwMDMvbG9uZy12ZWN0b3JzLW5vdC1zdXBwb3J0ZWQteWV0LWVycm9yLWluLXJtZC1idXQtbm90LWluLXItc2NyaXB0CiAgICAgICAgICAgICAgICAgICAgICAjIGNhY2hlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNhY2hlLmxhenkgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHJ1bnRpbWUgYWZ0ZXIgY2h1bmsKICAgICAgICAgICAgICAgICAgICAgIHRpbWVfaXQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBzYXZlIGZpZ3VyZXMgaW4gUERGIGluIGEgc2VwYXJhdGUgZm9sZGVyCiAgICAgICAgICAgICAgICAgICAgICBkZXYgPSBjKCdwbmcnLCAncGRmJyksICMgdGlmZiBvciBwZGYgYWxvbmUgcmVuZGVycyBiYWQgaW4gaHRtbAogICAgICAgICAgICAgICAgICAgICAgIyBkcGkgPSAzMDAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcucGF0aCA9ICJmaWd1cmVzX2RldGFpbC8iLAogICAgICAgICAgICAgICAgICAgICAgcGRmLm9wdGlvbnMoZW5jb2RpbmcgPSAiSVNPTGF0aW45LmVuYyIpKQpgYGAKCgpUaGlzIGZpbGUgaXMgdXNlZCB0byBwcmVwYXJlIHRoZSBmaWd1cmVzIGZvciB0aGUgcGFwZXIuCgpgYGB7ciBsaWJyYXJ5fQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KG9yZy5NbS5lZy5kYikKCi5saWJQYXRocygpCmBgYAoKCiMgUHJlcGFyYXRpb24KCkhlcmUgYXJlIHRoZSBmb2xkZXJzIHdoZXJlIGFuYWx5emVzIGFyZSBzdG9yZWQgOgoKYGBge3IgbG9jYXRpb25zfQpkYXRhX2RpciA9ICIuLy4uIgpsaXN0LmZpbGVzKGRhdGFfZGlyKQpgYGAKCgpXZSBsb2FkIHRoZSBkYXRhc2V0IGNvbnRhaW5pbmcgYWxsIGNlbGxzIDoKCmBgYHtyIGxvYWRfYWxsX3NvYmp9CnNvYmogPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi8zX2NvbWJpbmVkL2hzX2hkX3NvYmoucmRzIikpCnNvYmoKYGBgCgpUaGVzZSBhcmUgYWxsIHRoZSBzYW1wbGVzIGFuYWx5emVkIDoKCmBgYHtyIHNhbXBsZV9pbmZvLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDN9CnNhbXBsZV9pbmZvID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9zYW1wbGVfaW5mby5yZHMiKSkKCiMgTmIgY2VsbHMgYnkgZGF0YXNldAp0b19wbG90ID0gdGFibGUoc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSguLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIGBjb2xuYW1lczwtYChjKCJzYW1wbGVfaWRlbnRpZmllciIsICJuYl9jZWxscyIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHggPSAuLCB5ID0gc2FtcGxlX2luZm8sIGJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIikgCgojIHBhdGNod29yawpwbG90X2xpc3QgPSBhcXVhcml1czo6ZmlnX3Bsb3RfZ2IodG9fcGxvdCwgdGl0bGUgPSAiQXZhaWxhYmxlIGRhdGFzZXRzIikKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkgKwogIHBhdGNod29yazo6cGxvdF9sYXlvdXQoZGVzaWduID0gIkFcbkIiLCBoZWlnaHRzID0gYygwLjEsNSkpICYKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTUpKQpgYGAKClRoZXNlIGFyZSB0aGUgY3VzdG9tIGNvbG9ycyBmb3IgY2VsbCBwb3B1bGF0aW9ucyA6CgpgYGB7ciBjb2xvcl9tYXJrZXJzLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9jb2xvcl9tYXJrZXJzLnJkcyIpKQpjb2xvcl9tYXJrZXJzID0gY29sb3JfbWFya2Vyc1tuYW1lcyhjb2xvcl9tYXJrZXJzKSAhPSAibWVsYW5vY3l0ZXMiXQpvcnNfY29sb3IgPSBjb2xvcl9tYXJrZXJzWyJPUlMiXQpjb2xvcl9tYXJrZXJzWyJPUlMiXSA9IGNvbG9yX21hcmtlcnNbIklGRSJdIApjb2xvcl9tYXJrZXJzWyJJRkUiXSA9IG9yc19jb2xvcgpjb2xvcl9tYXJrZXJzWyJCIGNlbGxzIl0gPSAiY2hvY29sYXRlMyIKcm0ob3JzX2NvbG9yKQoKIyByZS1vcmRlcgpjb2xvcl9tYXJrZXJzID0gY29sb3JfbWFya2Vyc1tjKCJDRDQgVCBjZWxscyIsICJDRDggVCBjZWxscyIsICJMYW5nZXJoYW5zIGNlbGxzIiwgIm1hY3JvcGhhZ2VzIiwgIkIgY2VsbHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdXRpY2xlIiwgImNvcnRleCIsICJtZWR1bGxhIiwgIklSUyIsICJwcm9saWZlcmF0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSEZTQyIsICJPUlMiLCAiSUJMIiwgIklGRSIsICJzZWJvY3l0ZXMiKV0KCmRhdGEuZnJhbWUoY2VsbF90eXBlID0gbmFtZXMoY29sb3JfbWFya2VycyksCiAgICAgICAgICAgY29sb3IgPSB1bmxpc3QoY29sb3JfbWFya2VycykpICU+JQogIGdncGxvdDI6OmdncGxvdCguLCBhZXMoeCA9IGNlbGxfdHlwZSwgeSA9IDAsIGZpbGwgPSBjZWxsX3R5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChwY2ggPSAyMSwgc2l6ZSA9IDUpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoY29sb3JfbWFya2VycyksIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIGFuZ2xlID0gMjApKQpgYGAKCldlIGRlZmluZSBjdXN0b20gY29sb3JzIGZvciBzYW1wbGUgdHlwZSA6CgpgYGB7ciBzYW1wbGVfdHlwZV9jb2xvcnMsIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQgPSAwLjc1LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc2FtcGxlX3R5cGVfY29sb3JzID0gc2V0TmFtZXMobm0gPSBsZXZlbHMoc2FtcGxlX2luZm8kc2FtcGxlX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCIjQzU1RjQwIiwgIiMyQzc4RTYiKSkKCmRhdGEuZnJhbWUoY2VsbF90eXBlID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSwKICAgICAgICAgICBjb2xvciA9IHVubGlzdChzYW1wbGVfdHlwZV9jb2xvcnMpKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSAwLCBmaWxsID0gY2VsbF90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQocGNoID0gMjEsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KHNhbXBsZV90eXBlX2NvbG9ycyksIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCldlIHNldCBhIGJhY2tncm91bmQgY29sb3IgOgoKYGBge3IgYmdfY29sb3J9CmJnX2NvbG9yID0gImdyYXk5NCIKYGBgCgoKVGhpcyBpcyB0aGUgY29ycmVzcG9uZGVuY2UgYmV0d2VlbiBjZWxsIHR5cGVzIGFuZCBjZWxsIGZhbWlsaWVzLCBhbmQgY3VzdG9tIGNvbG9ycyB0byBjb2xvciBjZWxscyBieSBjZWxsIGZhbWlseSA6CgpgYGB7ciBjZWxsX2ZhbWlseX0KY3VzdG9tX29yZGVyX2NlbGxfdHlwZSA9IGRhdGEuZnJhbWUoCiAgY2VsbF90eXBlID0gbmFtZXMoY29sb3JfbWFya2VycyksCiAgY2VsbF9mYW1pbHkgPSBjKHJlcCgiaW1tdW5lIGNlbGxzIiwgNSksCiAgICAgICAgICAgICAgICAgIHJlcCgibWF0cml4IiwgNSksCiAgICAgICAgICAgICAgICAgIHJlcCgibm9uIG1hdHJpeCIsIDUpKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmN1c3RvbV9vcmRlcl9jZWxsX3R5cGUkY2VsbF90eXBlID0gZmFjdG9yKGN1c3RvbV9vcmRlcl9jZWxsX3R5cGUkY2VsbF90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlJGNlbGxfdHlwZSkKcm93bmFtZXMoY3VzdG9tX29yZGVyX2NlbGxfdHlwZSkgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlJGNlbGxfdHlwZQoKZmFtaWx5X2NvbG9yID0gYygiaW1tdW5lIGNlbGxzIiA9ICJzbGF0ZWJsdWUxIiwKICAgICAgICAgICAgICAgICAibWF0cml4IiA9ICJtZWRpdW1zZWFncmVlbiIsCiAgICAgICAgICAgICAgICAgIm5vbiBtYXRyaXgiID0gImZpcmVicmljazMiKQpgYGAKCldlIGxvYWQgbWFya2VycyB0byBkaXNwbGF5IG9uIGEgZG90cGxvdCB0byBhc3Nlc3MgY2VsbCB0eXBlIGFubm90YXRpb24gOgoKYGBge3IgZG90cGxvdF9tYXJrZXJzfQpkb3RwbG90X21hcmtlcnMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi8xX21ldGFkYXRhL2hzX2hkX2RvdHBsb3RfbWFya2Vycy5yZHMiKSkKZG90cGxvdF9tYXJrZXJzID0gZG90cGxvdF9tYXJrZXJzW25hbWVzKGRvdHBsb3RfbWFya2VycykgIT0gIm1lbGFub2N5dGVzIl0KbGVuZ3Rocyhkb3RwbG90X21hcmtlcnMpCmBgYAoKQ3VzdG9tIGZ1bmN0aW9ucyB0byBkaXNwbGF5IGdlbmUgZXhwcmVzc2lvbiBvbiB0aGUgaGVhdG1hcCA6CgpgYGB7ciBjb2xvcl9mdW4sIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9mdW4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIGdlbmVfcmFuZ2UgPSByYW5nZShodF9hbm5vdFssIG9uZV9nZW5lXSkKICBnZW5lX3BhbGV0dGUgPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjb2xvcnMgPSBjKCIjRkZGRkZGIiwgYXF1YXJpdXM6OmNvbG9yX2dlbmVbLTFdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoZnJvbSA9IGdlbmVfcmFuZ2VbMV0sIHRvID0gZ2VuZV9yYW5nZVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IGxlbmd0aChhcXVhcml1czo6Y29sb3JfZ2VuZSkpKQogIHJldHVybihnZW5lX3BhbGV0dGUpCn0KYGBgCgoKIyBBbGwgc2FtcGxlcwoKIyMgU2V0dGluZ3MKClRoaXMgaXMgdGhlIHByb2plY3Rpb24gbmFtZSB0byB2aXN1YWxpemUgY2VsbHMgOgoKYGBge3IgYWxsX3NvYmpfbmFtZTJEfQpuYW1lMkQgPSAiaGFybW9ueV8zOF90c25lIgpuYW1lMkRfYXRsYXMgPSBuYW1lMkQKYGBgCgojIyBQcmVwYXJhdGlvbgoKV2UgbWFrZSBhIGxvdyByZXNvbHV0aXZlIGNsdXN0ZXJpbmcgZm9yIHRoZSBoZWF0bWFwIDoKCmBgYHtyIGNsdXN0ZXJfYWxsfQpzb2JqID0gU2V1cmF0OjpGaW5kQ2x1c3RlcnMoc29iaiwgcmVzb2x1dGlvbiA9IDAuNCkKCmxlbmd0aChsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpKQpgYGAKCgpXZSBkZWZpbmUgY2x1c3RlciB0eXBlIGFuZCBjbHVzdGVyIGZhbWlseSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfZmFtaWx5X2FsbCwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDV9CnNvYmokY2VsbF90eXBlID0gc29iaiRjZWxsX3R5cGUgJT4lCiAgYXMuY2hhcmFjdGVyKCkgJT4lCiAgZmFjdG9yKC4sIGxldmVscyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKQoKY2x1c3Rlcl90eXBlID0gdGFibGUoc29iaiRjZWxsX3R5cGUsIHNvYmokc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKY2x1c3Rlcl90eXBlID0gc2V0TmFtZXMobm0gPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoc29iaiRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqJGNsdXN0ZXJfdHlwZSA9IGNsdXN0ZXJfdHlwZVtzb2JqJHNldXJhdF9jbHVzdGVyc10Kc29iaiRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29iaiRjbHVzdGVyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGxldmVscyhzb2JqJGNlbGxfdHlwZSkpCnNvYmokY2x1c3Rlcl9mYW1pbHkgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlW3NvYmokY2x1c3Rlcl90eXBlLCAiY2VsbF9mYW1pbHkiXQpzb2JqJGNsdXN0ZXJfZmFtaWx5ID0gZmFjdG9yKHNvYmokY2x1c3Rlcl9mYW1pbHksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbmFtZXMoZmFtaWx5X2NvbG9yKSkKYGBgCgoKIyMgRmlndXJlcwoKUHJvamVjdCBuYW1lIDoKCmBgYHtyIGZpZzFfc2FtcGxlX2lkZW50aWZpZXIsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIFJhbmRvbSBvcmRlcgpzZXQuc2VlZCgxMjM0KQpybmRfb3JkZXIgPSBzYW1wbGUoY29sbmFtZXMoc29iaiksIHJlcGxhY2UgPSBGQUxTRSwgc2l6ZSA9IG5jb2woc29iaikpCgojIEV4dHJhY3QgY29vcmRpbmF0ZXMKY2VsbHNfY29vcmQgPSBzb2JqQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRwcm9qZWN0X25hbWUgPSBzb2JqJHByb2plY3RfbmFtZQpjZWxsc19jb29yZCA9IGNlbGxzX2Nvb3JkWyhybmRfb3JkZXIpLCBdCgojIFBsb3QKZ2dwbG90Mjo6Z2dwbG90KGNlbGxzX2Nvb3JkLCBhZXMoeCA9IERpbTEsIHkgPSBEaW0yLCBjb2wgPSBwcm9qZWN0X25hbWUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMC41KSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgpTYW1wbGUgdHlwZSA6CgpgYGB7ciBmaWcxX3NhbXBsZV90eXBlLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KIyBFeHRyYWN0IGNvb3JkaW5hdGVzCmNlbGxzX2Nvb3JkID0gc29iakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJEaW0xIiwgIkRpbTIiKSkKY2VsbHNfY29vcmQkc2FtcGxlX3R5cGUgPSBzb2JqJHNhbXBsZV90eXBlCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHNhbXBsZV90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkNsdXN0ZXIgOgoKYGBge3IgZmlnMV9jbHVzdGVyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KZ3JleV9wYWxldHRlID0gc2V0TmFtZXMobm0gPSBsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICByZXAoIiNEOUQ5RDkiLCBsZW5ndGgobGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSkpKQpncmV5X3BhbGV0dGVbYygiNyIsICIxNiIsICIxIiwgIjEyIiwgIjExIiwgIjEwIiwgIjE1IildID0gIiNCREJEQkQiCmdyZXlfcGFsZXR0ZVtjKCIxNiIsICIxNCIsICI1IiwgIjkiKV0gPSAiIzk2OTY5NiIKClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjQsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBjb2xzID0gZ3JleV9wYWxldHRlLAogICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gNikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDZWxsIHR5cGUgYW5ub3RhdGlvbiA6CgpgYGB7ciBmaWcxX2NlbGxfdHlwZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDbHVzdGVyIGZhbWlseSBhbm5vdGF0aW9uIDoKCmBgYHtyIGZpZzFfY2x1c3Rlcl9mYW1pbHksIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA2fQpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl9mYW1pbHkiLCBjb2xzID0gZmFtaWx5X2NvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNlbGwgdHlwZSBhbm5vdGF0aW9uIHNwbGl0IGJ5IGNvbmRpdGlvbiA6CgpgYGB7ciBmaWcxX2NlbGxfdHlwZV9zcGxpdCwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBsb3RfbGlzdCA9IGFxdWFyaXVzOjpwbG90X3NwbGl0X2RpbXJlZChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieSA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfY29sb3IgPSBjb2xvcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfY29sb3IgPSBzYW1wbGVfdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19jb2xvciA9IGJnX2NvbG9yKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkgJgogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkdlbmUgZXhwcmVzc2lvbiB0byBhc3Nlc3MgYW5ub3RhdGlvbiA6CgpgYGB7ciBmaWcxX2dlbmVfZmFtaWx5LCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KZ2VuZXMgPSBjKCJQVFBSQyIsICJNU1gyIiwgIktSVDE0IikKbmFtZXMoZ2VuZXMpID0gYygiaW1tdW5lIGNlbGxzIiwgIm1hdHJpeCBjZWxscyIsICJub24tbWF0cml4IGNlbGxzIikKCnBsb3RfbGlzdCA9IGxhcHBseShjKDE6bGVuZ3RoKGdlbmVzKSksIEZVTiA9IGZ1bmN0aW9uKGdlbmVfaWQpIHsKICBnZW5lID0gZ2VuZXNbW2dlbmVfaWRdXQogIHBvcCA9IG5hbWVzKGdlbmVzKVtnZW5lX2lkXQogIAogIHNvYmokbXlfZ2VuZSA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmosIGdlbmUpWywgMV0gJT4lCiAgICBhcXVhcml1czo6cnVuX3Jlc2NhbGUoLiwgbmV3X21pbiA9IDAsIG5ld19tYXggPSAxMCkKICAKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIGZlYXR1cmVzID0gIm15X2dlbmUiLCByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMTAsIGJ5ID0gMi41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJtaW4iLCByZXAoIiIsIDMpLCAibWF4IikpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lKSArIAogICAgIyBzdWJ0aXRsZSA9IHBvcCkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNyksCiAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTUpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgU2V1cmF0OjpOb0F4ZXMoKQp9KQoKcGxvdF9saXN0CmBgYAoKQmFycGxvdCBieSBjbHVzdGVyIGZhbWlseSA6CgpgYGB7ciBmaWcxX2JhcnBsb3RfZmFtaWx5LCBmaWcud2lkdGggPSAzLjUsIGZpZy5oZWlnaHQgPSAzLjV9CnF1YW50aWYgPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJuYl9jZWxscyIpKQoKYXF1YXJpdXM6OnBsb3RfYmFycGxvdChkZiA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqJGNsdXN0ZXJfZmFtaWx5KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbCBUeXBlIiwgIk51bWJlciIpKSwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlNhbXBsZSIsIHkgPSAiTnVtYmVyIiwgZmlsbCA9ICJDZWxsIFR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCgpKSArCiAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gLmRhdGEkU2FtcGxlLCB5ID0gMS4wNSwgbGFiZWwgPSAuZGF0YSRuYl9jZWxscyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCwgc2l6ZSA9IDQpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoZmFtaWx5X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhmYW1pbHlfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2VsbCBGYW1pbHkiKSArCiAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IDAuMjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoc2VxKDAsIDEwMCwgYnkgPSAyNSksIHNlcCA9ICIgJSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBnZ3Bsb3QyOjpleHBhbnNpb24oYWRkID0gYygwLCAwLjA1KSkpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImxpZ2h0Z3JheSIpLAogICAgICAgICAgICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IDI1LCByID0gMCwgYiA9IDAsIGwgPSAwKSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkhlYXRtYXAgb2YgY2x1c3RlciBwcm9wb3J0aW9uIGJ5IHNhbXBsZSA6CgpgYGB7ciBmaWcxX2hlYXRtYXBfcHJvcCwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDUsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpncm91cF9ieSA9ICJzZXVyYXRfY2x1c3RlcnMiCgpjbHVzdGVyX2J5X3NhbXBsZSA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc29iakBtZXRhLmRhdGFbLCBncm91cF9ieV0pICU+JQogIHByb3AudGFibGUobWFyZ2luID0gMSkgJT4lCiAgYXMubWF0cml4KCkKCiMjIFJpZ2h0IGFubm90YXRpb24gOiBudW1iZXIgb2YgY2VsbHMgYnkgZGF0YXNldApodF9hbm5vdCA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygic2FtcGxlX2lkZW50aWZpZXIiLCAibmJfY2VsbHMiKSkgJT4lCiAgYHJvd25hbWVzPC1gKC4kc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGRwbHlyOjpzZWxlY3QoLXNhbXBsZV9pZGVudGlmaWVyKQoKaGFfcmlnaHQgPSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPSBodF9hbm5vdCwKICB3aGljaCA9ICJyb3ciLAogIHNob3dfbGVnZW5kID0gVFJVRSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJ0b3AiLAogIGNvbCA9IGxpc3QobmJfY2VsbHMgID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiR3JleXMiLCBuID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoZnJvbSA9IHJhbmdlKGh0X2Fubm90JG5iX2NlbGxzKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IHJhbmdlKGh0X2Fubm90JG5iX2NlbGxzKVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gOSkpKSkKCiMjIExlZnQgYW5ub3RhdGlvbiA6IGdlbmRlcgpoYV9sZWZ0ID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGdlbmRlciA9IHNhbXBsZV9pbmZvJGdlbmRlciwKICB3aGljaCA9ICJyb3ciLAogIHNob3dfbGVnZW5kID0gVFJVRSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJ0b3AiLAogIGNvbCA9IGxpc3QoZ2VuZGVyID0gc2V0TmFtZXMobm0gPSBjKCJGIiwgIk0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImxpZ2h0Y3lhbjMiLCAibmF2eWJsdWUiKSkpKQoKIyMgVG9wIGFubm90YXRpb24gOiBtYWluIGNlbGwgdHlwZSBpbiB0aGlzIGNsdXN0ZXIKaHRfYW5ub3QgPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgIHNvYmpAbWV0YS5kYXRhWywgZ3JvdXBfYnldKSAlPiUKICBwcm9wLnRhYmxlKG1hcmdpbiA9IDEpICU+JQogIGFzLm1hdHJpeCgpCgpodF9hbm5vdCA9IHRhYmxlKHNvYmokY2VsbF90eXBlLAogICAgICAgICAgICAgICAgIHNvYmpAbWV0YS5kYXRhWywgZ3JvdXBfYnldKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKaHRfYW5ub3QgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IG5hbWVzKGh0X2Fubm90KSwKICAgICAgICAgICAgICAgICAgICAgIGNlbGxfdHlwZSA9IG5hbWVzKGNvbG9yX21hcmtlcnMpW2h0X2Fubm90XSwKICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKaHRfYW5ub3QgPSBkcGx5cjo6bGVmdF9qb2luKGh0X2Fubm90LCBjdXN0b21fb3JkZXJfY2VsbF90eXBlLCBieSA9ICJjZWxsX3R5cGUiKSAlPiUKICAjIFNpbXBsaWZpY2F0aW9uIGZvciBtYXRyaXgKICBkcGx5cjo6bXV0YXRlKGNlbGxfdHlwZSA9IGlmZWxzZShjZWxsX3R5cGUgJWluJSBjKCJtZWR1bGxhIiwgImNvcnRleCIsICJjdXRpY2xlIiksIHllcyA9ICJoYWlyIHNoYWZ0Iiwgbm8gPSBjZWxsX3R5cGUpKSAlPiUKICAjIFNpbXBsaWZpY2F0aW9uIGZvciBUIGNlbGxzCiAgZHBseXI6Om11dGF0ZShjZWxsX3R5cGUgPSBpZmVsc2UoY2VsbF90eXBlICVpbiUgYygiQ0Q0IFQgY2VsbHMiLCAiQ0Q4IFQgY2VsbHMiKSwgeWVzID0gIlQgY2VsbHMiLCBubyA9IGNlbGxfdHlwZSkpICU+JQogICMgU2ltcGxpZmljYXRpb24gZm9yIEFQQwogIGRwbHlyOjptdXRhdGUoY2VsbF90eXBlID0gaWZlbHNlKGNlbGxfdHlwZSAlaW4lIGMoIkxhbmdlcmhhbnMgY2VsbHMiLCAibWFjcm9waGFnZXMiKSwgeWVzID0gIkFQQyIsIG5vID0gY2VsbF90eXBlKSkgJT4lCiAgIyBBZGQgY29sb3IKICBkcGx5cjo6bXV0YXRlKGNvbG9yID0gYXMuY2hhcmFjdGVyKGNvbG9yX21hcmtlcnNbY2VsbF90eXBlXSkpICU+JQogIGRwbHlyOjptdXRhdGUoY29sb3IgPSBpZmVsc2UoY2VsbF90eXBlID09ICJoYWlyIHNoYWZ0IiwgeWVzID0gIiNGRkI2QzEiLCBubyA9IGNvbG9yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb2xvciA9IGlmZWxzZShjZWxsX3R5cGUgPT0gIlQgY2VsbHMiLCB5ZXMgPSAiIzhBNkVFNiIsIG5vID0gY29sb3IpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNvbG9yID0gaWZlbHNlKGNlbGxfdHlwZSA9PSAiQVBDIiwgeWVzID0gIiM5Q0FBNEIiLCBubyA9IGNvbG9yKSkKCmhhX3RvcCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICAjIGNlbGxfdHlwZSA9IGh0X2Fubm90JGNlbGxfdHlwZSwKICBjZWxsX2ZhbWlseSA9IGh0X2Fubm90JGNlbGxfZmFtaWx5LAogIHdoaWNoID0gImNvbHVtbiIsCiAgc2hvd19sZWdlbmQgPSBUUlVFLAogIHNob3dfYW5ub3RhdGlvbl9uYW1lID0gRkFMU0UsCiAgIyBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IiwKICBjb2wgPSBsaXN0KCNjZWxsX3R5cGUgPSBzZXROYW1lcyhubSA9IGh0X2Fubm90JGNlbGxfdHlwZSwKICAgICMgICAgICAgICAgICAgICAgICAgICAgaHRfYW5ub3QkY29sb3IpLAogICAgY2VsbF9mYW1pbHkgPSBmYW1pbHlfY29sb3IKICApKQoKIyMgQXNzZW1ibGUgaGVhdG1hcApodCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKGNsdXN0ZXJfYnlfc2FtcGxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJQcm9wb3J0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gYygiIzIxNjZBQyIsICIjRjdGN0Y3IiwgIiNCMjE4MkIiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBib3R0b21fYW5ub3RhdGlvbiA9IGhhX2JvdHRvbSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWdodF9hbm5vdGF0aW9uID0gaGFfcmlnaHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVmdF9hbm5vdGF0aW9uID0gaGFfbGVmdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhhX3RvcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X3RpdGxlID0gIlNhbXBsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3BhcihuYW1lcyA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIkNsdXN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19jZW50ZXJlZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X25hbWVzX3NpZGUgPSAibGVmdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX3NpZGUgPSAidG9wIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfcm90ID0gMCkKCiMjIERyYXcgIQpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwgbWVyZ2VfbGVnZW5kcyA9IFRSVUUpCmBgYAoKRm9yIHRoZSBkb3RwbG90LCB3ZSBjbGFyaWZ5IGNsdXN0ZXJzIGFuZCBjZWxsIHR5cGUgYW5ub3RhdGlvbiA6CgpgYGB7ciBpbXByb3ZlX2N1c3RvbV9vcmRlcl9jZWxsX3R5cGV9CmNlbGxfdHlwZV9pbl9jbHVzdGVyID0gdGFibGUoc29iaiRjZWxsX3R5cGUsIHNvYmokc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDEpICU+JQogIGFwcGx5KC4sIDEsIHdoaWNoLm1heCkKY2VsbF90eXBlX2luX2NsdXN0ZXIgPSBjZWxsX3R5cGVfaW5fY2x1c3RlciAtIDEKCm1pc3NpbmdfY2x1c3RlciA9IHNldGRpZmYobGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsX3R5cGVfaW5fY2x1c3RlcikKCmNlbGxfdHlwZV9pbl9jbHVzdGVyID0gZGF0YS5mcmFtZShjZWxsX3R5cGUgPSBjKG5hbWVzKGNlbGxfdHlwZV9pbl9jbHVzdGVyKSwgY2x1c3Rlcl90eXBlW21pc3NpbmdfY2x1c3Rlcl0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9pZCA9IGMoY2VsbF90eXBlX2luX2NsdXN0ZXIsIG5hbWVzKGNsdXN0ZXJfdHlwZVttaXNzaW5nX2NsdXN0ZXJdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIHJvdy5uYW1lcyA9IE5VTEwpICU+JQogIGRwbHlyOjptdXRhdGUoY2x1c3Rlcl9pZCA9IGFzLm51bWVyaWMoY2x1c3Rlcl9pZCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGNlbGxfdHlwZSwgY2x1c3Rlcl9pZCkKCmN1c3RvbV9vcmRlcl9jZWxsX3R5cGUkY2x1c3RlcnMgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlICU+JQogIGFwcGx5KC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKG9uZV9yb3cpIHsKICAgIGNlbGxfdHlwZSA9IG9uZV9yb3dbImNlbGxfdHlwZSJdCiAgICBjbHVzdGVycyA9IGNlbGxfdHlwZV9pbl9jbHVzdGVyICU+JQogICAgICBkcGx5cjo6ZmlsdGVyKC5kYXRhJGNlbGxfdHlwZSA9PSAuZW52JGNlbGxfdHlwZSkgJT4lCiAgICAgIGRwbHlyOjpwdWxsKGNsdXN0ZXJfaWQpCiAgICAKICAgIGNlbGxfdHlwZV9jbHVzdGVyID0gcGFzdGUwKGNlbGxfdHlwZSwgIiAoIiwgcGFzdGUwKGNsdXN0ZXJzLCBjb2xsYXBzZSA9ICIsICIpLCAiKSIpCiAgICAKICAgIHJldHVybihjZWxsX3R5cGVfY2x1c3RlcikKICB9KSAlPiUKICBmYWN0b3IoLiwgbGV2ZWxzID0gLikKCmN1c3RvbV9vcmRlcl9jZWxsX3R5cGUKYGBgCgpEb3RwbG90IDoKCmBgYHtyIGZpZzFfZG90cGxvdCwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDguNSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnBsb3RfbGlzdCA9IGFxdWFyaXVzOjpwbG90X2RvdHBsb3Qoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXJzID0gYygiUFRQUkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDNFIiwgIkNENCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEM0UiLCAiQ0Q4QSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEMjA3IiwgIkFJRjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUUkVNMiIsICJNU1IxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0Q3OUEiLCAiQ0Q3OUIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgIlBSRE0xIiwgIktSVDg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTVNYMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDMyIiwgIktSVDM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUMzEiLCAiUFJSOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJBTUJJIiwgIkFMREgxQTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQ3MSIsICJLUlQ3MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRPUDJBIiwgIk1DTTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNCIsICJDWENMMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNSIsICJDT0wxN0ExIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRElPMiIsICJUQ0VBTDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNiIsICJLUlQ2QyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNQSU5LNSIsICJMWTZEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0xNUCIsICJQUEFSRyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsIGNvbHVtbl9uYW1lID0gImNlbGxfdHlwZSIsIG5iX2hsaW5lID0gMCkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3ggPSAidmVydGljYWwiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsNzAsMCwwKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQocmVwKDAsIDQpLCAiY20iKSkKCnAgPSBnZ3Bsb3QyOjpnZ3Bsb3QoY3VzdG9tX29yZGVyX2NlbGxfdHlwZSwgYWVzKHggPSBjbHVzdGVycywgeSA9IDApKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMCkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeCA9IDAuNSwgeGVuZCA9IDUuNSwgeSA9IDAsIHllbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGZhbWlseV9jb2xvclsiaW1tdW5lIGNlbGxzIl0pICsKICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoYWVzKHggPSA1LjUsIHhlbmQgPSAxMC41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gZmFtaWx5X2NvbG9yWyJtYXRyaXgiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeCA9IDEwLjUsIHhlbmQgPSAxNS41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gZmFtaWx5X2NvbG9yWyJub24gbWF0cml4Il0pICsKICBnZ3Bsb3QyOjpzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApLCBsaW1pdHMgPSBjKDAsMCkpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAsMC41LDAuNSwwKSwgImNtIikpCgpwbG90X2xpc3QgPSBwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDEsIGhlaWdodHMgPSBjKDI1LCAxKSkKcGxvdF9saXN0CmBgYAoKCiMgSW1tdW5lIGNlbGxzCgojIyBTZXR0aW5ncwoKV2UgbG9hZCB0aGUgaW1tdW5lIGNlbGxzIGRhdGFzZXQgOgoKYGBge3Igc29ial9pY30Kc29ial9pYyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8xX3pvb21faW1tdW5lL2ltbXVuZV9jZWxsc19zb2JqLnJkcyIpKQpzb2JqX2ljCmBgYAoKClRoaXMgaXMgdGhlIHByb2plY3Rpb24gbmFtZSB0byB2aXN1YWxpemUgY2VsbHMgOgoKYGBge3Igc29ial9uYW1lMkRfaWN9Cm5hbWUyRCA9ICJoYXJtb255XzIwX3RzbmUiCmBgYAoKVG8gcmVwcmVzZW50IHJlc3VsdHMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgd2UgbG9hZCB0aGUgYW5hbHlzZXMgcmVzdWx0cyA6CgpgYGB7ciBsaXN0X3Jlc3VsdHNfaWN9Cmxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8xX3pvb21faW1tdW5lL2ltbXVuZV9jZWxsc19saXN0X3Jlc3VsdHMucmRzIikpCgpsYXBwbHkobGlzdF9yZXN1bHRzLCBGVU4gPSBuYW1lcykKYGBgCgoKIyMgUHJlcGFyYXRpb24KCldlIGRlZmluZWQgY2x1c3RlciB0eXBlIGFuZCBjbHVzdGVyIGZhbWlseSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfZmFtaWx5X2ljLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KY2x1c3Rlcl90eXBlID0gdGFibGUoc29ial9pYyRjZWxsX3R5cGUsIHNvYmpfaWMkc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKY2x1c3Rlcl90eXBlID0gc2V0TmFtZXMobm0gPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoc29ial9pYyRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqX2ljJGNsdXN0ZXJfdHlwZSA9IGNsdXN0ZXJfdHlwZVtzb2JqX2ljJHNldXJhdF9jbHVzdGVyc10Kc29ial9pYyRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29ial9pYyRjbHVzdGVyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGxldmVscyhzb2JqX2ljJGNlbGxfdHlwZSkpCmBgYAoKCiMjIEZpZ3VyZXMKCkNvbnRyb2wgY2VsbHMgb24gdGhlIGZ1bGwgYXRsYXMgOgoKYGBge3IgZmlnX2ljX2xvY2F0aW9uLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0Kc29iaiRpc19pbW11bmUgPSAoY29sbmFtZXMoc29iaikgJWluJSBjb2xuYW1lcyhzb2JqX2ljKSkKClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkRfYXRsYXMsIHB0LnNpemUgPSAwLjAwMDAwMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImlzX2ltbXVuZSIsIG9yZGVyID0gIlRSVUUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoZmFtaWx5X2NvbG9yW1siaW1tdW5lIGNlbGxzIl1dLCBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoVFJVRSwgRkFMU0UpKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJJbW11bmUgY2VsbHMiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobmNvbChzb2JqX2ljKSwgIiBjZWxscyIpKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDbHVzdGVyIHR5cGUgOgoKYGBge3IgZmlnX2ljX2NsdXN0ZXJfdHlwZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqX2ljLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKVmlvbGluIHBsb3Qgb2YgSUwxQiBpbiBtYWNyb3BoYWdlcyA6CgpgYGB7ciBmaWdfaWNfbWFjX2lsMWIsIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyLjV9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYywgc2V1cmF0X2NsdXN0ZXJzID09IDIpCnRhYmxlKHN1YnNvYmokc2FtcGxlX3R5cGUpCgppbDFiX2hzID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIklMMUIiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCmlsMWJfaGQgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUwxQiIsIHN1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhEIl0KaWwxYl9oc19WU19pbDFiX2hkID0gc3RhdHM6OnQudGVzdChpbDFiX2hzLCBpbDFiX2hkKQppbDFiX2hzX1ZTX2lsMWJfaGQKClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIHB0LnNpemUgPSAwLjMsCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJJTDFCIiwgY29scyA9IHNhbXBsZV90eXBlX2NvbG9ycykgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKU3BsaXQgYnkgc2FtcGxlIDoKCmBgYHtyIGZpZ19pY19tYWNfaWwxYl9zcGxpdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJJTDFCIiwgY29scyA9IHNhbXBsZV9pbmZvJGNvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpWaW9saW4gcGxvdCBvZiBJTDFCIGluIG1hY3JvcGhhZ2VzIDoKCmBgYHtyIGZpZ19pY19tYWNfaWw2LCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMi41fQppbDZfaHMgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUw2Iiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQppbDZfaGQgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUw2Iiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQppbDZfaHNfVlNfaWw2X2hkID0gc3RhdHM6OnQudGVzdChpbDZfaHMsIGlsNl9oZCkKaWw2X2hzX1ZTX2lsNl9oZAoKU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwgcHQuc2l6ZSA9IDAuMywKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIklMNiIsIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKClZpb2xpbiBwbG90IG9mIFRORiBpbiBtYWNyb3BoYWdlcyA6CgpgYGB7ciBmaWdfaWNfbWFjX3RuZiwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDIuNX0KdG5mX2hzID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIlRORiIsIHN1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhTIl0KdG5mX2hkID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIlRORiIsIHN1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhEIl0KdG5mX2hzX1ZTX3RuZl9oZCA9IHN0YXRzOjp0LnRlc3QodG5mX2hzLCB0bmZfaGQpCnRuZl9oc19WU190bmZfaGQKClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIHB0LnNpemUgPSAwLjMsCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJUTkYiLCBjb2xzID0gc2FtcGxlX3R5cGVfY29sb3JzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpTcGxpdCBieSBzYW1wbGUgOgoKYGBge3IgZmlnX2ljX21hY190bmZfc3BsaXQsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX2lkZW50aWZpZXIiLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiVE5GIiwgY29scyA9IHNhbXBsZV9pbmZvJGNvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKVmlvbGluIHBsb3Qgb2YgR1pNQSBpbiBDRDQgVCBjZWxscyA6CgpgYGB7ciBmaWdfaWNfdDRfZ3ptYSwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDIuNX0Kc3Vic29iaiA9IHN1YnNldChzb2JqX2ljLCBzZXVyYXRfY2x1c3RlcnMgJWluJSBjKDAsMTApKQp0YWJsZShzdWJzb2JqJHNhbXBsZV90eXBlKQoKZ3ptYV9ocyA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJHWk1BIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQpnem1hX2hkID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIkdaTUEiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCmd6bWFfaHNfVlNfZ3ptYV9oZCA9IHN0YXRzOjp0LnRlc3QoZ3ptYV9ocywgZ3ptYV9oZCkKZ3ptYV9oc19WU19nem1hX2hkCgpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLCBwdC5zaXplID0gMC4zLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiR1pNQSIsIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKClZpb2xpbiBwbG90IG9mIElGTkcgaW4gQ0Q0IFQgY2VsbHMgOgoKYGBge3IgZmlnX2ljX3Q0X2lmbmcsIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyLjV9CmlmbmdfaHMgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUZORyIsIHN1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhTIl0KaWZuZ19oZCA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJJRk5HIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQppZm5nX2hzX1ZTX2lmbmdfaGQgPSBzdGF0czo6dC50ZXN0KGlmbmdfaHMsIGlmbmdfaGQpCmlmbmdfaHNfVlNfaWZuZ19oZAoKU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwgcHQuc2l6ZSA9IDAuMywKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIklGTkciLCBjb2xzID0gc2FtcGxlX3R5cGVfY29sb3JzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgClZpb2xpbiBwbG90IG9mIElMMTdBIGluIENENCBUIGNlbGxzIDoKCmBgYHtyIGZpZ19pY190NF9JTDE3LCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMi41fQpJTDE3X2hzID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIklMMTdBIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQpJTDE3X2hkID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIklMMTdBIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQpJTDE3X2hzX1ZTX0lMMTdfaGQgPSBzdGF0czo6dC50ZXN0KElMMTdfaHMsIElMMTdfaGQpCklMMTdfaHNfVlNfSUwxN19oZAoKU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwgcHQuc2l6ZSA9IDAuMywKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIklMMTdSRSIsIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCldlIHJlcHJlc2VudCBzb21lIGdlbmVzIHNwbGl0IGJ5IHNhbXBsZSB0eXBlIDoKCmBgYHtyIGZpZ19pY19nZW5lX3NwbGl0LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gM30KcGxvdF9saXN0ID0gbGFwcGx5KGMoIklMMUIiLCAiR1pNQSIsICJJRk5HIiwgIklMMTdBIiwgIlRORiIsICJJTDYiKSwgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBwID0gYXF1YXJpdXM6OnBsb3Rfc3BsaXRfZGltcmVkKHNvYmpfaWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdF9ieSA9ICJzYW1wbGVfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcl9ieSA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfcGFsZXR0ZSA9IGMoImdyYXk3MCIsICIjRkRCQjg0IiwgIiNFRjY1NDgiLCAiIzdGMDAwMCIsICJibGFjayIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbl9wdF9zaXplID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmdfcHRfc2l6ZSA9IDAuNiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJnX2NvbG9yID0gImdyYXk5NSIpCiAgcCA9IHBhdGNod29yazo6d3JhcF9wbG90cyhwLCBucm93ID0gMSkgKwogICAgcGF0Y2h3b3JrOjpwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpICsKICAgIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICYKICAgIGdncGxvdDI6OnRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgcmV0dXJuKHApCn0pCgpwbG90X2xpc3QKYGBgCgpCYXJwbG90IGJ5IGNsdXN0ZXIgdHlwZSA6CgpgYGB7ciBmaWdfaWNfYmFycGxvdCwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CnF1YW50aWYgPSB0YWJsZShzb2JqX2ljJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJuYl9jZWxscyIpKQoKYXF1YXJpdXM6OnBsb3RfYmFycGxvdChkZiA9IHRhYmxlKHNvYmpfaWMkc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2ljJGNsdXN0ZXJfdHlwZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIkNlbGwgVHlwZSIsICJOdW1iZXIiKSksCiAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJTYW1wbGUiLCB5ID0gIk51bWJlciIsIGZpbGwgPSAiQ2VsbCBUeXBlIiwKICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKCkpICsKICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBxdWFudGlmLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSAuZGF0YSRTYW1wbGUsIHkgPSA1MCArIC5kYXRhJG5iX2NlbGxzLCBsYWJlbCA9IC5kYXRhJG5iX2NlbGxzKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAwLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNlbGwgVHlwZSIpICsKICBnZ3Bsb3QyOjpzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxMDAgKyBtYXgocXVhbnRpZiRuYl9jZWxscykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBnZ3Bsb3QyOjpleHBhbnNpb24oYWRkID0gYygwLCAwLjA1KSkpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImxpZ2h0Z3JheSIpLAogICAgICAgICAgICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKSGVhdG1hcCBmb3IgbWFjcm9waGFnZXMgOgoKYGBge3IgZmlnX2ljX2hlYXRtYXBfbWFjcm9waGFnZXMsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1LjIsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpzdWJzb2JqID0gc3Vic2V0KHNvYmpfaWMsIGNsdXN0ZXJfdHlwZSA9PSAibWFjcm9waGFnZXMiKQpmZWF0dXJlc19vaSA9IGMoIklMMUIiLCAiVE5GIiwKICAgICAgICAgICAgICAgICJITEEtRFFBMiIsICJITEEtRFBBMSIsICJITEEtRFJCNSIsCiAgICAgICAgICAgICAgICAiSExBLUEiLCAiSExBLUMiLCAiQjJNIiwKICAgICAgICAgICAgICAgICJDMVFBIiwgIkMxUUIiLCAiQzFRQyIpCgojIE1hdHJpeAptYXRfZXhwciA9IFNldXJhdDo6R2V0QXNzYXlEYXRhKHN1YnNvYmopCm1hdF9leHByID0gbWF0X2V4cHJbZmVhdHVyZXNfb2ksIF0KbWF0X2V4cHIgPSBNYXRyaXg6OnQobWF0X2V4cHIpCm1hdF9leHByID0gZHludXRpbHM6OnNjYWxlX3F1YW50aWxlKG1hdF9leHByKSAjIGJldHdlZW4gMCBhbmQgMQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKZGltKG1hdF9leHByKSAjIGdlbmVzIHggY2VsbHMKIyMgQ29sb3JzCmxpc3RfY29sb3JzID0gbGlzdCgpCgojIEhlYXRtYXAKbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dID0gcmV2KFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIlJkQnUiLCBuID0gOSkpCgojIFNhbXBsZSBhbm5vdGF0aW9uICh0b3AgYW5ub3RhdGlvbikKbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSA9IHNhbXBsZV90eXBlX2NvbG9ycwpsaXN0X2NvbG9yc1tbInNhbXBsZV9pZGVudGlmaWVyIl1dID0gc2V0TmFtZXMobm0gPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pbmZvJGNvbG9yKQojIENlbGxzIG9yZGVyCmNvbHVtbl9vcmRlciA9IHN1YnNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjphcnJhbmdlKHNhbXBsZV90eXBlLCBzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgcm93bmFtZXMoKQpjb2x1bW5fb3JkZXIgPSBtYXRjaChjb2x1bW5fb3JkZXIsIHJvd25hbWVzKHN1YnNvYmpAbWV0YS5kYXRhKSkKCiMgSGVhdG1hcApoYV90b3AgPSBIZWF0bWFwQW5ub3RhdGlvbihzYW1wbGVfdHlwZSA9IHN1YnNvYmokc2FtcGxlX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gc3Vic29iaiRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChzYW1wbGVfdHlwZSA9IGxpc3RfY29sb3JzW1sic2FtcGxlX3R5cGUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV9pZGVudGlmaWVyIl1dKSkKCiMgSGVhdG1hcApodCA9IEhlYXRtYXAoYXMubWF0cml4KG1hdF9leHByKSwKICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJFeHByZXNzaW9uIiwgYXQgPSBjKDAsIDEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJsb3ciLCAiaGlnaCIpKSwKICAgICAgICAgICAgIGNvbCA9IGxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSwKICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfdG9wLAogICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgIGNvbHVtbl9vcmRlciA9IGNvbHVtbl9vcmRlciwKICAgICAgICAgICAgIGNvbHVtbl9nYXAgPSB1bml0KDIsICJtbSIpLAogICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsCiAgICAgICAgICAgICByb3dfdGl0bGUgPSBOVUxMLAogICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDE0LCBmb250ZmFjZSA9ICJwbGFpbiIpLAogICAgICAgICAgICAgdXNlX3Jhc3RlciA9IEZBTFNFLAogICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICBib3JkZXIgPSBUUlVFKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJib3R0b20iKQpgYGAKCkhlYXRtYXAgZm9yIENENCBUIGNlbGxzIDoKCmBgYHtyIGZpZ19pY19oZWF0bWFwX2NkNCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDQsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpzdWJzb2JqID0gc3Vic2V0KHNvYmpfaWMsIGNsdXN0ZXJfdHlwZSA9PSAiQ0Q0IFQgY2VsbHMiKQpmZWF0dXJlc19vaSA9IGMoIkdaTUEiLCAiS0xSQjEiLCAiQlRHMSIsICJaRlAzNiIsICJORktCSUEiLCAiVFhOSVAiLCAiQ1hDUjQiLCAiSUZORyIsICJJTDE3QSIpCgojIE1hdHJpeAptYXRfZXhwciA9IFNldXJhdDo6R2V0QXNzYXlEYXRhKHN1YnNvYmopCm1hdF9leHByID0gbWF0X2V4cHJbZmVhdHVyZXNfb2ksIF0KbWF0X2V4cHIgPSBNYXRyaXg6OnQobWF0X2V4cHIpCm1hdF9leHByID0gZHludXRpbHM6OnNjYWxlX3F1YW50aWxlKG1hdF9leHByKSAjIGJldHdlZW4gMCBhbmQgMQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKZGltKG1hdF9leHByKSAjIGdlbmVzIHggY2VsbHMKIyMgQ29sb3JzCmxpc3RfY29sb3JzID0gbGlzdCgpCgojIEhlYXRtYXAKbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dID0gcmV2KFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIlJkQnUiLCBuID0gOSkpCgojIFNhbXBsZSBhbm5vdGF0aW9uICh0b3AgYW5ub3RhdGlvbikKbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSA9IHNhbXBsZV90eXBlX2NvbG9ycwpsaXN0X2NvbG9yc1tbInNhbXBsZV9pZGVudGlmaWVyIl1dID0gc2V0TmFtZXMobm0gPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pbmZvJGNvbG9yKQojIENlbGxzIG9yZGVyCmNvbHVtbl9vcmRlciA9IHN1YnNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjphcnJhbmdlKHNhbXBsZV90eXBlLCBzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgcm93bmFtZXMoKQpjb2x1bW5fb3JkZXIgPSBtYXRjaChjb2x1bW5fb3JkZXIsIHJvd25hbWVzKHN1YnNvYmpAbWV0YS5kYXRhKSkKCiMgSGVhdG1hcApoYV90b3AgPSBIZWF0bWFwQW5ub3RhdGlvbihzYW1wbGVfdHlwZSA9IHN1YnNvYmokc2FtcGxlX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gc3Vic29iaiRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChzYW1wbGVfdHlwZSA9IGxpc3RfY29sb3JzW1sic2FtcGxlX3R5cGUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV9pZGVudGlmaWVyIl1dKSkKCiMgSGVhdG1hcApodCA9IEhlYXRtYXAoYXMubWF0cml4KG1hdF9leHByKSwKICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJFeHByZXNzaW9uIiwgYXQgPSBjKDAsIDEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJsb3ciLCAiaGlnaCIpKSwKICAgICAgICAgICAgIGNvbCA9IGxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSwKICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfdG9wLAogICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgIGNvbHVtbl9vcmRlciA9IGNvbHVtbl9vcmRlciwKICAgICAgICAgICAgIGNvbHVtbl9nYXAgPSB1bml0KDIsICJtbSIpLAogICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsCiAgICAgICAgICAgICByb3dfdGl0bGUgPSBOVUxMLAogICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDE0LCBmb250ZmFjZSA9ICJwbGFpbiIpLAogICAgICAgICAgICAgdXNlX3Jhc3RlciA9IEZBTFNFLAogICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICBib3JkZXIgPSBUUlVFKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAibGVmdCIsCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbGVnZW5kX3NpZGUgPSAibGVmdCIpCmBgYAoKCiMgSEZTQwoKIyMgU2V0dGluZ3MKCldlIGxvYWQgdGhlIEhGU0NzIGRhdGFzZXQgOgoKYGBge3Igc29ial9oZnNjfQpzb2JqX2hmc2MgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vMl96b29tX2hmc2MvaGZzY19zb2JqLnJkcyIpKQpzb2JqX2hmc2MKYGBgCgoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBuYW1lIHRvIHZpc3VhbGl6ZSBjZWxscyA6CgpgYGB7ciBzb2JqX25hbWUyRF9oZnNjfQpuYW1lMkQgPSAiaGFybW9ueV8yNF90c25lIgpgYGAKClRvIHJlcHJlc2VudCByZXN1bHRzIGZyb20gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIHdlIGxvYWQgdGhlIGFuYWx5c2VzIHJlc3VsdHMgOgoKYGBge3IgbGlzdF9yZXN1bHRzX2hmc2N9Cmxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8yX3pvb21faGZzYy9oZnNjX2xpc3RfcmVzdWx0cy5yZHMiKSkKCmxhcHBseShsaXN0X3Jlc3VsdHMsIEZVTiA9IG5hbWVzKQpgYGAKCiMjIEZpZ3VyZXMKCkhGU0NzIG9uIHRoZSBmdWxsIGF0bGFzIDoKCmBgYHtyIGZpZ19oZnNjX2xvY2F0aW9uLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0Kc29iaiRpc19oZnNjID0gKGNvbG5hbWVzKHNvYmopICVpbiUgY29sbmFtZXMoc29ial9oZnNjKSkKClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkRfYXRsYXMsIHB0LnNpemUgPSAwLjAwMDAwMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImlzX2hmc2MiLCBvcmRlciA9ICJUUlVFIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGNvbG9yX21hcmtlcnNbWyJIRlNDIl1dLCBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoVFJVRSwgRkFMU0UpKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJIRlNDcyIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChuY29sKHNvYmpfaGZzYyksICIgY2VsbHMiKSkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKS1JUMTUgZXhwcmVzc2lvbiA6CgpgYGB7ciBmaWdfaGZzY19rcnQxNSwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDJ9ClNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJEX2F0bGFzLCBwdC5zaXplID0gMC4wMDAwMDEsCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiS1JUMTUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpHZW5lcyBvZiBpbnRlcmVzdCA6CgpgYGB7ciBmaWdfaGZzY19nZW5lcywgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CmdlbmVzID0gYygiVEdGQjIiLCAiQU5HUFRMNyIsICJGR0YxOCIsICJNR1AiLCAiRVBDQU0iLCAiS1JUNzUiLCAiTk9UQ0gzIiwgIlBUSExIIikKCnBsb3RfbGlzdCA9IGxhcHBseShjKDE6bGVuZ3RoKGdlbmVzKSksIEZVTiA9IGZ1bmN0aW9uKGdlbmVfaWQpIHsKICBnZW5lID0gZ2VuZXNbW2dlbmVfaWRdXQogIAogIHNvYmpfaGZzYyRteV9nZW5lID0gU2V1cmF0OjpGZXRjaERhdGEoc29ial9oZnNjLCBnZW5lKVssIDFdICU+JQogICAgYXF1YXJpdXM6OnJ1bl9yZXNjYWxlKC4sIG5ld19taW4gPSAwLCBuZXdfbWF4ID0gMTApCiAgCiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqX2hmc2MsIGZlYXR1cmVzID0gIm15X2dlbmUiLCByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMTAsIGJ5ID0gMi41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJtaW4iLCByZXAoIiIsIDMpLCAibWF4IikpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE3KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwbG90X2xpc3QKYGBgCgpQcm9qZWN0IG5hbWUgOgoKYGBge3IgZmlnX2hmc2NfcHJvamVjdF9uYW1lLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KIyBSYW5kb20gb3JkZXIKc2V0LnNlZWQoMTIzNCkKcm5kX29yZGVyID0gc2FtcGxlKGNvbG5hbWVzKHNvYmpfaGZzYyksIHJlcGxhY2UgPSBGQUxTRSwgc2l6ZSA9IG5jb2woc29ial9oZnNjKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpfaGZzY0ByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJEaW0xIiwgIkRpbTIiKSkKY2VsbHNfY29vcmQkcHJvamVjdF9uYW1lID0gc29ial9oZnNjJHByb2plY3RfbmFtZQpjZWxsc19jb29yZCA9IGNlbGxzX2Nvb3JkWyhybmRfb3JkZXIpLCBdCgojIFBsb3QKZ2dwbG90Mjo6Z2dwbG90KGNlbGxzX2Nvb3JkLCBhZXMoeCA9IERpbTEsIHkgPSBEaW0yLCBjb2wgPSBwcm9qZWN0X25hbWUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMS4yKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkNsdXN0ZXIgOgoKYGBge3IgZmlnX2hmc2NfY2x1c3RlcnMsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29ial9oZnNjLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwgY29scyA9IGdyZXlfcGFsZXR0ZSwKICAgICAgICAgICAgICAgIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDcpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKSGVhdG1hcCB3aXRoIHByb3BvcnRpb25zIDoKCmBgYHtyIGZpZ19oZnNjX2hlYXRtYXAsIGZpZy53aWR0aCA9IDUuNSwgZmlnLmhlaWdodCA9IDUuNSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CmNsdXN0ZXJfbWFya2VycyA9IGMoIlRHRkIyIiwgIkFOR1BUTDciLCAiRVBDQU0iLCAiS1JUNzUiLCAiTk9UQ0gzIiwgIlBUSExIIikKCiMjIEJvdHRvbSBhbm5vdGF0aW9uIDogZ2VuZSBleHByZXNzaW9uIGJ5IGNsdXN0ZXIKaHRfYW5ub3QgPSBTZXVyYXQ6OkZldGNoRGF0YShzb2JqX2hmc2MsIHNsb3QgPSAiZGF0YSIsIHZhcnMgPSBjbHVzdGVyX21hcmtlcnMpICU+JQogIGFzLmRhdGEuZnJhbWUoKQpodF9hbm5vdCRjbHVzdGVycyA9IHNvYmpfaGZzYyRzZXVyYXRfY2x1c3RlcnMKaHRfYW5ub3QgPSBodF9hbm5vdCAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoY2x1c3RlcnMpICU+JQogIGRwbHlyOjpzdW1tYXJpc2VfYWxsKGZ1bnMoJ21lYW4nID0gbWVhbikpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1jbHVzdGVycykgJT4lCiAgYGNvbG5hbWVzPC1gKGMoY2x1c3Rlcl9tYXJrZXJzKSkKCmhhX2JvdHRvbSA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbihkZiA9IGh0X2Fubm90LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2ggPSAiY29sdW1uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IHNldE5hbWVzKG5tID0gY2x1c3Rlcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFwcGx5KGNsdXN0ZXJfbWFya2VycywgRlVOID0gY29sb3JfZnVuKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IikKCiMjIFJpZ2h0IGFubm90YXRpb24gOiBudW1iZXIgb2YgY2VsbHMgYnkgZGF0YXNldApodF9hbm5vdCA9IHRhYmxlKHNvYmpfaGZzYyRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJzYW1wbGVfaWRlbnRpZmllciIsICJuYl9jZWxscyIpKSAlPiUKICBgcm93bmFtZXM8LWAoLiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgZHBseXI6OnNlbGVjdCgtc2FtcGxlX2lkZW50aWZpZXIpCgpoYV9yaWdodCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICBkZiA9IGh0X2Fubm90LAogIHdoaWNoID0gInJvdyIsCiAgc2hvd19sZWdlbmQgPSBUUlVFLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIsCiAgY29sID0gbGlzdChuYl9jZWxscyAgPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmFtZSA9ICJHcmV5cyIsIG4gPSA5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcShmcm9tID0gcmFuZ2UoaHRfYW5ub3QkbmJfY2VsbHMpWzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gcmFuZ2UoaHRfYW5ub3QkbmJfY2VsbHMpWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSA5KSkpKQoKIyMgSGVhdG1hcApodCA9IGFxdWFyaXVzOjpwbG90X3Byb3BfaGVhdG1hcChkZiA9IHNvYmpfaGZzY0BtZXRhLmRhdGFbLCBjKCJzYW1wbGVfaWRlbnRpZmllciIsICJzZXVyYXRfY2x1c3RlcnMiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvdHRvbV9hbm5vdGF0aW9uID0gaGFfYm90dG9tLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHJpZ2h0X2Fubm90YXRpb24gPSBoYV9yaWdodCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX2NlbnRlcmVkID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcF9tYXJnaW4gPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKG5hbWVzID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfdGl0bGUgPSAiU2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIkNsdXN0ZXIiKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAiYm90dG9tIikKYGBgCgpIZWF0bWFwIGZvciBjbHVzdGVyIDAgYW5kIDggOgoKYGBge3IgZmlnX2hmc2NfaGVhdG1hcF9jbHVzdGVyMDgsIGZpZy53aWR0aCA9IDUuNSwgZmlnLmhlaWdodCA9IDEwLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc3Vic29iaiA9IHN1YnNldChzb2JqX2hmc2MsIHNldXJhdF9jbHVzdGVycyAlaW4lIGMoMCw4KSkKCmZlYXR1cmVzX29pID0gcm93bmFtZXMobGlzdF9yZXN1bHRzJGNsdXN0ZXJfMF84KQpmZWF0dXJlc19vaSA9IGZlYXR1cmVzX29pWyFncmVwbChmZWF0dXJlc19vaSwgcGF0dGVybiA9ICJeUlAiKV0KCiMgTWF0cml4Cm1hdF9leHByID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaikKbWF0X2V4cHIgPSBtYXRfZXhwcltmZWF0dXJlc19vaSwgXQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKbWF0X2V4cHIgPSBjYmluZChtYXRfZXhwciwgc3Vic29iaiRwZXJjZW50Lm10KQpjb2xuYW1lcyhtYXRfZXhwcilbbmNvbChtYXRfZXhwcildID0gInBlcmNlbnQucmIiCm1hdF9leHByID0gZHludXRpbHM6OnNjYWxlX3F1YW50aWxlKG1hdF9leHByKSAjIGJldHdlZW4gMCBhbmQgMQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKZGltKG1hdF9leHByKSAjIGdlbmVzIHggY2VsbHMKIyMgQ29sb3JzCmxpc3RfY29sb3JzID0gbGlzdCgpCgojIEhlYXRtYXAKbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dID0gcmV2KFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIlJkQnUiLCBuID0gOSkpCgojIFNhbXBsZSBhbm5vdGF0aW9uICh0b3AgYW5ub3RhdGlvbikKbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSA9IHNhbXBsZV90eXBlX2NvbG9ycwpsaXN0X2NvbG9yc1tbInNhbXBsZV9pZGVudGlmaWVyIl1dID0gc2V0TmFtZXMobm0gPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pbmZvJGNvbG9yKQojIGxpc3RfY29sb3JzW1sic2V1cmF0X2NsdXN0ZXJzIl1dID0gc2V0TmFtZXMoYXF1YXJpdXM6OmdnX2NvbG9yX2h1ZShsZW5ndGgobGV2ZWxzKHN1YnNvYmokc2V1cmF0X2NsdXN0ZXJzKSkpLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBsZXZlbHMoc3Vic29iaiRzZXVyYXRfY2x1c3RlcnMpKQojIENlbGxzIG9yZGVyCmNvbHVtbl9vcmRlciA9IHN1YnNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjphcnJhbmdlKHNhbXBsZV90eXBlLCBzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgcm93bmFtZXMoKQpjb2x1bW5fb3JkZXIgPSBtYXRjaChjb2x1bW5fb3JkZXIsIHJvd25hbWVzKHN1YnNvYmpAbWV0YS5kYXRhKSkKCiMgSGVhdG1hcApoYV90b3AgPSBIZWF0bWFwQW5ub3RhdGlvbihzYW1wbGVfdHlwZSA9IHN1YnNvYmokc2FtcGxlX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gc3Vic29iaiRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjbHVzdGVycyA9IHN1YnNvYmokc2V1cmF0X2NsdXN0ZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KHNhbXBsZV90eXBlID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IGxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNsdXN0ZXJzID0gbGlzdF9jb2xvcnNbWyJzZXVyYXRfY2x1c3RlcnMiXV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkKCgojIGcxIDogUkVBQ1RPTUVfQ1lUT0tJTkVfU0lHTkFMSU5HX0lOX0lNTVVORV9TWVNURU0KIyBnMiA6IEdPQlBfQVBPUFRPVElDX1BST0NFU1MKZzFfZ2VuZXMgPSBjKCJCMk0iLCAiSExBLUMiLCAiSExBLUEiLCAiTUlGIiwgIlBQSUEiLCAiSlVOQiIsICJJRklUTTMiKQpnMl9nZW5lcyA9IGMoIkp1biIsICJBVEYzIiwgIkJURzIiLCAiUkhPQiIsICJORktCSUEiLCAiU0dLMSIsICJLTEY5IiwKICAgICAgICAgICAgICJDQVYxIiwgIkRESVQ0IiwgIlBESzQiLCAiVFhOSVAiLCAiUk5GMTE1MiIsICJUTEUxIikKaGFfcmlnaHQgPSBkYXRhLmZyYW1lKGdlbmVzID0gIGMoZmVhdHVyZXNfb2ksICJwZXJjZW50LnJiIiksIHJvd25hbWVzID0gYyhmZWF0dXJlc19vaSwgInBlcmNlbnQucmIiKSkKaGFfcmlnaHQkZ3JvdXAgPSBjYXNlX3doZW4oaGFfcmlnaHQkZ2VuZXMgJWluJSBnMV9nZW5lcyB+ICJSRUFDVE9NRV9DWVRPS0lORV9TSUdOQUxJTkdfSU5fSU1NVU5FX1NZU1RFTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGhhX3JpZ2h0JGdlbmVzICVpbiUgZzJfZ2VuZXMgfiAiR09CUF9BUE9QVE9USUNfUFJPQ0VTUyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAib3RoZXJzIikKCmxpc3RfY29sb3JzW1siZ3JvdXAiXV0gPSBzZXROYW1lcygKICBubSA9IGMoIlJFQUNUT01FX0NZVE9LSU5FX1NJR05BTElOR19JTl9JTU1VTkVfU1lTVEVNIiwgIkdPQlBfQVBPUFRPVElDX1BST0NFU1MiLCAib3RoZXJzIiksCiAgYygicmVkIiwgImJsYWNrIiwgImdyYXk5MCIpKQoKaGFfcmlnaHQgPSBIZWF0bWFwQW5ub3RhdGlvbihncm91cCA9IGhhX3JpZ2h0JGdyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3QoZ3JvdXAgPSBsaXN0X2NvbG9yc1tbImdyb3VwIl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaCA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfYW5ub3RhdGlvbl9uYW1lID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19sZWdlbmQgPSBUUlVFKQoKIyBIZWF0bWFwCmh0ID0gSGVhdG1hcChhcy5tYXRyaXgobWF0X2V4cHIpLAogICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkV4cHJlc3Npb24iLCBhdCA9IGMoMCwgMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImxvdyIsICJoaWdoIikpLAogICAgICAgICAgICAgY29sID0gbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dLAogICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoYV90b3AsCiAgICAgICAgICAgICByaWdodF9hbm5vdGF0aW9uID0gaGFfcmlnaHQsCiAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgY29sdW1uX29yZGVyID0gY29sdW1uX29yZGVyLAogICAgICAgICAgICAgY29sdW1uX2dhcCA9IHVuaXQoMiwgIm1tIiksCiAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgICAgIHJvd190aXRsZSA9IE5VTEwsCiAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTAsIGZvbnRmYWNlID0gInBsYWluIiksCiAgICAgICAgICAgICB1c2VfcmFzdGVyID0gRkFMU0UsCiAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgIGJvcmRlciA9IFRSVUUpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIpCmBgYAoKVmlvbGluIHBsb3Qgb2YgSUZJVE0zIDoKCmBgYHtyIGZpZ19oZnNjX2lmaXRtMywgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodCA9IDR9CnRhYmxlKHN1YnNvYmokc2FtcGxlX3R5cGUpCgppZml0bTNfaHMgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUZJVE0zIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQppZml0bTNfaGQgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUZJVE0zIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQppZml0bTNfaHNfVlNfaWZpdG0zX2hkID0gc3RhdHM6OnQudGVzdChpZml0bTNfaHMsIGlmaXRtM19oZCkKaWZpdG0zX2hzX1ZTX2lmaXRtM19oZAoKU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIklGSVRNMyIsIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKClNwbGl0IGJ5IHNhbXBsZSA6CgpgYGB7ciBmaWdfaGZzY19pZml0bTNfc3BsaXQsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX2lkZW50aWZpZXIiLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiSUZJVE0zIiwgY29scyA9IHNhbXBsZV9pbmZvJGNvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKVmlvbGluIHBsb3Qgb2YgRERJVDQgOgoKYGBge3IgZmlnX2hmc2NfZGRpdDQsIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQgPSA0fQp0YWJsZShzdWJzb2JqJHNhbXBsZV90eXBlKQoKRERJVDRfaHMgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiRERJVDQiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCkRESVQ0X2hkID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIkRESVQ0Iiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQpERElUNF9oc19WU19ERElUNF9oZCA9IHN0YXRzOjp0LnRlc3QoRERJVDRfaHMsIERESVQ0X2hkKQpERElUNF9oc19WU19ERElUNF9oZAoKU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIkRESVQ0IiwgY29scyA9IHNhbXBsZV90eXBlX2NvbG9ycykgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKU3BsaXQgYnkgc2FtcGxlIDoKCmBgYHtyIGZpZ19oZnNjX2RkaXQ0X3NwbGl0LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIkRESVQ0IiwgY29scyA9IHNhbXBsZV9pbmZvJGNvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpXZSByZXByZXNlbnQgc29tZSBnZW5lcyBzcGxpdCBieSBzYW1wbGUgdHlwZSA6CgpgYGB7ciBmaWdfaGZzY19nZW5lX3NwbGl0LCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CnBsb3RfbGlzdCA9IGxhcHBseShjKCJERElUNCIsICJJRklUTTMiKSwgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBwID0gYXF1YXJpdXM6OnBsb3Rfc3BsaXRfZGltcmVkKHNvYmpfaGZzYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2J5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gb25lX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcl9wYWxldHRlID0gYygiZ3JheTcwIiwgIiNGREJCODQiLCAiI0VGNjU0OCIsICIjN0YwMDAwIiwgImJsYWNrIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluX3B0X3NpemUgPSAwLjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19wdF9zaXplID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmdfY29sb3IgPSAiZ3JheTk1IikKICBwID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHAsIG5yb3cgPSAxKSArCiAgICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgKwogICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgJgogICAgZ2dwbG90Mjo6dGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKICByZXR1cm4ocCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAyKQpgYGAKCgpCYXJwbG90IHdpdGggbnVtYmVyIG9mIEhGU0NzIGFuZCB0b3RhbCBudW1iZXIgb2YgY2VsbHMgOgoKYGBge3IgZmlnX2hmc2NfYmFycGxvdCwgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gNC41fQpxdWFudGlmID0gZHBseXI6OmxlZnRfam9pbigKICB4ID0gdGFibGUoc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIm5iX2NlbGxzIikpLAogIHkgPSB0YWJsZShzb2JqX2hmc2Mkc2FtcGxlX2lkZW50aWZpZXIpICU+JQogICAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogICAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJuYl9oZnNjIikpLAogIGJ5ID0gIlNhbXBsZSIpICU+JQogIGRwbHlyOjptdXRhdGUocHJvcF9oZnNjID0gcm91bmQoMTAwKm5iX2hmc2MgLyBuYl9jZWxscywgMikpCgpxdWFudGlmX3RvX3Bsb3QgPSByYmluZC5kYXRhLmZyYW1lKAogIGRhdGEuZnJhbWUoU2FtcGxlID0gcXVhbnRpZiRTYW1wbGUsCiAgICAgICAgICAgICBuYl9jZWxscyA9IHF1YW50aWYkbmJfY2VsbHMgLSBxdWFudGlmJG5iX2hmc2MsCiAgICAgICAgICAgICBjZWxsX3R5cGUgPSAib3RoZXJzIiwKICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSksCiAgZGF0YS5mcmFtZShTYW1wbGUgPSBxdWFudGlmJFNhbXBsZSwKICAgICAgICAgICAgIG5iX2NlbGxzID0gcXVhbnRpZiRuYl9oZnNjLAogICAgICAgICAgICAgY2VsbF90eXBlID0gImhmc2MiLAogICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjZWxsX3R5cGUgPSBmYWN0b3IoY2VsbF90eXBlLCBsZXZlbHMgPSBjKCJvdGhlcnMiLCAiaGZzYyIpKSkKCmFxdWFyaXVzOjpwbG90X2JhcnBsb3QoZGYgPSBxdWFudGlmX3RvX3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJTYW1wbGUiLCB5ID0gIm5iX2NlbGxzIiwgZmlsbCA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCgpKSArCiAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gLmRhdGEkU2FtcGxlLCB5ID0gMC4wNSsuZGF0YSRwcm9wX2hmc2MvMTAwLCBsYWJlbCA9IC5kYXRhJG5iX2hmc2MpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IDAsIHNpemUgPSA1LCBmaWxsID0gTkEpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJncmF5OTAiLCBjb2xvcl9tYXJrZXJzW1siSEZTQyJdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygib3RoZXJzIiwgImhmc2MiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNlbGwgVHlwZSIpICsKICBnZ3Bsb3QyOjpzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEsIGJ5ID0gMC4yNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHBhc3RlMChzZXEoMCwgMTAwLCBieSA9IDI1KSwgc2VwID0gIiAlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IGdncGxvdDI6OmV4cGFuc2lvbihhZGQgPSBjKDAsIDAuMDUpKSkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAibGlnaHRncmF5IiksCiAgICAgICAgICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojIElCTCBhbmQgT1JTCgojIyBTZXR0aW5ncwoKV2UgbG9hZCB0aGUgSUJMICsgT1JTIGRhdGFzZXQgOgoKYGBge3Igc29ial9pYmxvcnN9CnNvYmpfaWJsb3JzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzNfem9vbV9pYmxtb3JzL2libG1vcnNfc29iai5yZHMiKSkKc29ial9pYmxvcnMKYGBgCgoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBuYW1lIHRvIHZpc3VhbGl6ZSBjZWxscyA6CgpgYGB7ciBzb2JqX25hbWUyRF9pYmxvcnN9Cm5hbWUyRCA9ICJoYXJtb255XzIwX3RzbmUiCmBgYAoKVG8gcmVwcmVzZW50IHJlc3VsdHMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgd2UgbG9hZCB0aGUgYW5hbHlzZXMgcmVzdWx0cyA6CgpgYGB7ciBsaXN0X3Jlc3VsdHNfaWJsb3JzfQpsaXN0X3Jlc3VsdHMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vM196b29tX2libG1vcnMvaWJsbW9yc19saXN0X3Jlc3VsdHMucmRzIikpCgpsYXBwbHkobGlzdF9yZXN1bHRzLCBGVU4gPSBuYW1lcykKYGBgCgojIyBQcmVwYXJhdGlvbgoKV2UgZGVmaW5lZCBjbHVzdGVyIHR5cGUgOgoKYGBge3IgY2x1c3Rlcl90eXBlX2libG9ycywgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDV9CmNsdXN0ZXJfdHlwZSA9IHRhYmxlKHNvYmpfaWJsb3JzJGNlbGxfdHlwZSwgc29ial9pYmxvcnMkc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKY2x1c3Rlcl90eXBlID0gc2V0TmFtZXMobm0gPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoc29ial9pYmxvcnMkY2VsbF90eXBlKVtjbHVzdGVyX3R5cGVdKQoKc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlID0gY2x1c3Rlcl90eXBlW3NvYmpfaWJsb3JzJHNldXJhdF9jbHVzdGVyc10Kc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlID0gZmFjdG9yKHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIklCTCIsICJPUlMiKSkKYGBgCgojIyBGaWd1cmVzCgpJQkwgKyBPUlMgb24gdGhlIGZ1bGwgYXRsYXMgOgoKYGBge3IgZmlnX2libG9yc19sb2NhdGlvbiwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDJ9CnNvYmokY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmopCnNvYmpfaWJsb3JzJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqX2libG9ycykKc29iaiRpc19pYmxvcnMgPSBkcGx5cjo6bGVmdF9qb2luKHNvYmpAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJwZXJjZW50Lm10IildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ial9pYmxvcnNAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJjbHVzdGVyX3R5cGUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJjZWxsX2JjIilbLCAiY2x1c3Rlcl90eXBlIl0KClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkRfYXRsYXMsIHB0LnNpemUgPSAwLjAwMDAwMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImlzX2libG9ycyIsIG9yZGVyID0gIlRSVUUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoY29sb3JfbWFya2Vyc1tjKCJJQkwiLCAiT1JTIildLCBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIklCTCIsICJPUlMiLCBOQSksIG5hLnZhbHVlID0gYmdfY29sb3IpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIklCTCArIE9SUyIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChuY29sKHNvYmpfaWJsb3JzKSwgIiBjZWxscyIpKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgoKUHJvamVjdCBuYW1lIDoKCmBgYHtyIGZpZ19pYmxvcnNfcHJvamVjdF9uYW1lLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KIyBSYW5kb20gb3JkZXIKc2V0LnNlZWQoMTIzNCkKcm5kX29yZGVyID0gc2FtcGxlKGNvbG5hbWVzKHNvYmpfaWJsb3JzKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqX2libG9ycykpCgojIEV4dHJhY3QgY29vcmRpbmF0ZXMKY2VsbHNfY29vcmQgPSBzb2JqX2libG9yc0ByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJEaW0xIiwgIkRpbTIiKSkKY2VsbHNfY29vcmQkcHJvamVjdF9uYW1lID0gc29ial9pYmxvcnMkcHJvamVjdF9uYW1lCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHByb2plY3RfbmFtZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAxLjIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKQ2x1c3RlciA6CgpgYGB7ciBmaWdfaWJsb3JzX2NsdXN0ZXJzLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmpfaWJsb3JzLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwgY29scyA9IGdyZXlfcGFsZXR0ZSwKICAgICAgICAgICAgICAgIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKQ2x1c3RlciB0eXBlIDoKCmBgYHtyIGZpZ19pYmxvcnNfY2x1c3Rlcl90eXBlLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmpfaWJsb3JzLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKQ2x1c3RlciB0eXBlIHNwbGl0IGJ5IHNhbXBsZSB0eXBlIDoKCmBgYHtyIGZpZ19pYmxvcnNfY2x1c3Rlcl90eXBlX3NwbGl0LCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNH0KcGxvdF9saXN0ID0gYXF1YXJpdXM6OnBsb3Rfc3BsaXRfZGltcmVkKHNvYmpfaWJsb3JzLCByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieSA9ICJjbHVzdGVyX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfY29sb3IgPSBjb2xvcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmdfcHRfc2l6ZSA9IDEsIG1haW5fcHRfc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19jb2xvciA9IGJnX2NvbG9yKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkgJgogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCk51bWJlciBvZiBjZWxscyBieSBjbHVzdGVyIGFuZCBieSBzYW1wbGUgOgoKYGBge3IgaWJsX29yc190YWJsZV9jbHVzdGVyc30KdGFibGUoc29ial9pYmxvcnMkc2V1cmF0X2NsdXN0ZXJzLAogICAgICBzb2JqX2libG9ycyRzYW1wbGVfaWRlbnRpZmllcikKCnRhYmxlKHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSwKICAgICAgc29ial9pYmxvcnMkc2FtcGxlX2lkZW50aWZpZXIpCmBgYAoKU2VwYXJhdGUgY2x1c3RlciA1IDoKCmBgYHtyIGlibF9vcnNfdGFibGVfY2x1c3RlcnNfc2VwNX0Kc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlX3NlcDUgPSBpZmVsc2Uoc29ial9pYmxvcnMkc2V1cmF0X2NsdXN0ZXJzID09IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJPUlNfNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gYXMuY2hhcmFjdGVyKHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSkpICU+JQogIGFzLmZhY3RvcigpCgp0YWJsZShzb2JqX2libG9ycyRjbHVzdGVyX3R5cGVfc2VwNSwKICAgICAgc29ial9pYmxvcnMkc2FtcGxlX3R5cGUpCmBgYAoKCkJhcnBsb3QgYnkgY2x1c3RlciBmYW1pbHkgOgoKYGBge3IgZmlnX2libG9yc19iYXJwbG90LCBmaWcud2lkdGggPSA1LjUsIGZpZy5oZWlnaHQgPSA0LjV9CnF1YW50aWYgPSB0YWJsZShzb2JqX2libG9ycyRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAibmJfY2VsbHMiKSkKCnF1YW50aWZfdG9fcGxvdCA9IHRhYmxlKHNvYmpfaWJsb3JzJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2libG9ycyRjbHVzdGVyX3R5cGVfc2VwNSkgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbFR5cGUiLCAiTnVtYmVyIikpICU+JQogIGRwbHlyOjptdXRhdGUoU3R5bGUgPSBpZmVsc2UoQ2VsbFR5cGUgPT0gIk9SU181IiwgeWVzID0gIklMMVIyKyIsIG5vID0gIklMMVIyLSIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKFN0eWxlID0gZmFjdG9yKFN0eWxlLCBsZXZlbHMgPSBjKCJJTDFSMi0iLCAiSUwxUjIrIikpKSAlPiUKICBkcGx5cjo6bXV0YXRlKENlbGxUeXBlID0gaWZlbHNlKENlbGxUeXBlID09ICJPUlNfNSIsIHllcyA9ICJPUlMiLCBubyA9IGFzLmNoYXJhY3RlcihDZWxsVHlwZSkpKSAlPiUKICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIkNlbGwgVHlwZSIsICJOdW1iZXIiLCAiSUwxUjIgc3RhdHVzIikpCgphcXVhcml1czo6cGxvdF9iYXJwbG90KGRmID0gcXVhbnRpZl90b19wbG90LAogICAgICAgICAgICAgICAgICAgICAgIHggPSAiU2FtcGxlIiwgeSA9ICJOdW1iZXIiLAogICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiQ2VsbCBUeXBlIiwgcGF0dGVybiA9ICJJTDFSMiBzdGF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCgpKSArCiAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gLmRhdGEkU2FtcGxlLCB5ID0gMS4wNSwgbGFiZWwgPSAuZGF0YSRuYl9jZWxscyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCwgc2l6ZSA9IDUpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoY29sb3JfbWFya2Vyc1tsZXZlbHMoc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlKV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnNbbGV2ZWxzKHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSldKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNlbGwgVHlwZSIpICsKICBnZ3Bsb3QyOjpzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEsIGJ5ID0gMC4yNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHBhc3RlMChzZXEoMCwgMTAwLCBieSA9IDI1KSwgc2VwID0gIiAlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IGdncGxvdDI6OmV4cGFuc2lvbihhZGQgPSBjKDAsIDAuMDUpKSkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAibGlnaHRncmF5IiksCiAgICAgICAgICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKRm9jdXMgb24gY2x1c3RlciA1LCBhbW9uZyBPUlMgOgoKYGBge3IgZmlnX2libG9yc19iYXJwbG90X2NsdXN0ZXI1LCBmaWcud2lkdGggPSA1LjUsIGZpZy5oZWlnaHQgPSA0LjV9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYmxvcnMsIGNsdXN0ZXJfdHlwZSA9PSAiT1JTIikKc3Vic29iaiRjbHVzdGVyX3R5cGVfc2VwNSA9IGlmZWxzZShzdWJzb2JqJHNldXJhdF9jbHVzdGVycyA9PSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJPUlNfNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSBhcy5jaGFyYWN0ZXIoc3Vic29iaiRjbHVzdGVyX3R5cGUpKSAlPiUKICBhcy5mYWN0b3IoKQoKcXVhbnRpZiA9IHRhYmxlKHN1YnNvYmokc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIm5iX2NlbGxzIikpCgpxdWFudGlmX3RvX3Bsb3QgPSB0YWJsZShzdWJzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICBzdWJzb2JqJGNsdXN0ZXJfdHlwZV9zZXA1KSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJDZWxsVHlwZSIsICJOdW1iZXIiKSkgJT4lCiAgZHBseXI6Om11dGF0ZShTdHlsZSA9IGlmZWxzZShDZWxsVHlwZSA9PSAiT1JTXzUiLCB5ZXMgPSAiSUwxUjIrIiwgbm8gPSAiSUwxUjItIikpICU+JQogIGRwbHlyOjptdXRhdGUoU3R5bGUgPSBmYWN0b3IoU3R5bGUsIGxldmVscyA9IGMoIklMMVIyLSIsICJJTDFSMisiKSkpICU+JQogIGRwbHlyOjptdXRhdGUoQ2VsbFR5cGUgPSBpZmVsc2UoQ2VsbFR5cGUgPT0gIk9SU181IiwgeWVzID0gIk9SUyIsIG5vID0gYXMuY2hhcmFjdGVyKENlbGxUeXBlKSkpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbCBUeXBlIiwgIk51bWJlciIsICJJTDFSMiBzdGF0dXMiKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbiguLCB5ID0gcXVhbnRpZiwgYnkgPSAiU2FtcGxlIikgJT4lCiAgZHBseXI6Om11dGF0ZShwcm9wX2NsdXN0ZXI1ID0gcm91bmQoTnVtYmVyL25iX2NlbGxzLCA0KSkKCmFxdWFyaXVzOjpwbG90X2JhcnBsb3QoZGYgPSBxdWFudGlmX3RvX3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJTYW1wbGUiLCB5ID0gIk51bWJlciIsCiAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJJTDFSMiBzdGF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCgpKSArCiAgIyBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBxdWFudGlmLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICMgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IC5kYXRhJFNhbXBsZSwgeSA9IDEuMDUsIGxhYmVsID0gLmRhdGEkbmJfY2VsbHMpLAogICMgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCwgc2l6ZSA9IDUpICsKICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBxdWFudGlmX3RvX3Bsb3QgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoYElMMVIyIHN0YXR1c2AgPT0gIklMMVIyKyIpLAogICAgICAgICAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gLmRhdGEkU2FtcGxlLCB5ID0gcHJvcF9jbHVzdGVyNSArIDAuMDUsIGxhYmVsID0gLmRhdGEkTnVtYmVyKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAwLCBzaXplID0gNSwgZmlsbCA9IE5BLCBjb2wgPSAid2hpdGUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCBjb2xvcl9tYXJrZXJzW1siT1JTIl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJJTDFSMisiLCAiSUwxUjItIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJJTDFSMiBzdGF0dXMiKSArCiAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IDAuMjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoc2VxKDAsIDEwMCwgYnkgPSAyNSksIHNlcCA9ICIgJSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBnZ3Bsb3QyOjpleHBhbnNpb24oYWRkID0gYygwLCAwLjA1KSkpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImxpZ2h0Z3JheSIpLAogICAgICAgICAgICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCgpERSBnZW5lcyBiZXR3ZWVuIElCTCBhbmQgT1JTIDoKCmBgYHtyIGZpZ19pYmxvcnNfZGVfcG9wLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0KbWFyayA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJG1hcmsKbWFyayRnZW5lX25hbWUgPSByb3duYW1lcyhtYXJrKQptYXJrX2xhYmVsID0gcmJpbmQoCiAgIyB1cC1yZWd1bGF0ZWQgaW4gSUJMCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSBhdmdfbG9nRkMpLAogICMgdXAtcmVndWxhdGVkIGluIE9SUwogIG1hcmsgJT4lIGRwbHlyOjp0b3BfbiguLCBuID0gMjAsIHd0ID0gLWF2Z19sb2dGQyksCiAgIyByZXByZXNlbnRhdGl2ZSBhbmQgc2VsZWN0aXZlIGZvciBJQkwKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IChwY3QuMSAtIHBjdC4yKSksCiAgIyByZXByZXNlbnRhdGl2ZSBhbmQgc2VsZWN0aXZlIGZvciBPUlMKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IC0ocGN0LjEgLSBwY3QuMikpKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKQptYXJrX2xhYmVsID0gbWFya19sYWJlbFshZ3JlcGwocm93bmFtZXMobWFya19sYWJlbCksIHBhdHRlcm4gPSAiXk1UIiksIF0KCmF2Z19sb2dGQ19yYW5nZSA9IHNldE5hbWVzKGMobWluKG1hcmtfbGFiZWwkYXZnX2xvZ0ZDKSwgLTEsIDAsIDEsIG1heChtYXJrX2xhYmVsJGF2Z19sb2dGQykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IGMoImRvZGdlcmJsdWU0IiwgImRvZGdlcmJsdWUzIiwgIiNCN0I3QjciLCAiZmlyZWJyaWNrMyIsICJmaXJlYnJpY2s0IikpCgoKZ2dwbG90Mjo6Z2dwbG90KG1hcmssIGFlcyh4ID0gcGN0LjEsIHkgPSBwY3QuMiwgY29sID0gYXZnX2xvZ0ZDKSkgKwogIGdncGxvdDI6Omdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbHR5ID0gMikgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gbWFya19sYWJlbCwgbWF4Lm92ZXJsYXBzID0gSW5mLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBwY3QuMSwgeSA9IHBjdC4yLCBsYWJlbCA9IGdlbmVfbmFtZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAzLjUsIGxhYmVsLnNpemUgPSBOQSkgKwogIGdncGxvdDI6OmxhYnMoeCA9ICJFbnJpY2hlZCBpbiBJQkwiLAogICAgICAgICAgICAgICAgeSA9ICJFbnJpY2hlZCBpbiBPUlMiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IG5hbWVzKGF2Z19sb2dGQ19yYW5nZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IHNjYWxlczo6cmVzY2FsZSh1bm5hbWUoYXZnX2xvZ0ZDX3JhbmdlKSkpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmBgYAoKR1NFQSBwbG90IDoKCmBgYHtyIGZpZ19pYmxvcnNfa2VyYXRpbml6YXRpb24sIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQp0aGVfZ3NfbmFtZSA9ICJSRUFDVE9NRV9LRVJBVElOSVpBVElPTiIgCnRoZV9jb250ZW50ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYUByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihJRCA9PSB0aGVfZ3NfbmFtZSkKdGhlX3N1YnRpdGxlID0gcGFzdGUwKCJcbk5FUyA6ICIsIHJvdW5kKHRoZV9jb250ZW50JE5FUywgMiksCiAgICAgICAgICAgICAgICAgICAgICAiIHwgcHZhbHVlIDogIiwgcm91bmQodGhlX2NvbnRlbnQkcHZhbHVlLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICIgfCBzZXQgc2l6ZSA6ICIsIHRoZV9jb250ZW50JHNldFNpemUsICIgZ2VuZXMiKQoKZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRnc2VhLAogICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMywgMywgNSwgMykpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQpgYGAKCmBgYHtyIGZpZ19pYmxvcnNfaWZuYSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIkhBTExNQVJLX0lOVEVSRkVST05fR0FNTUFfUkVTUE9OU0UiIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJGdzZWFAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIoSUQgPT0gdGhlX2dzX25hbWUpCnRoZV9zdWJ0aXRsZSA9IHBhc3RlMCgiXG5ORVMgOiAiLCByb3VuZCh0aGVfY29udGVudCRORVMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIiB8IHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiIHwgc2V0IHNpemUgOiAiLCB0aGVfY29udGVudCRzZXRTaXplLCAiIGdlbmVzIikKCmVucmljaHBsb3Q6OmdzZWFwbG90Mih4ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYSwKICAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSB0aGVfc3VidGl0bGUpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDMsIDMsIDUsIDMpKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZ2d0ZXh0OjplbGVtZW50X21hcmtkb3duKGhqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwKSkKYGBgCgpTY29yZSBmb3IgYm90aCBnZW5lIHNldHMsIGluIGFsbCBjZWxscyA6CgpgYGB7ciBmaWdfaWJsb3JzX2tlcmFfc2NvcmUsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpnZW5lX3NldHMgPSBhcXVhcml1czo6Z2V0X2dlbmVfc2V0cyhzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIpCnRoZV9nc19uYW1lID0gIlJFQUNUT01FX0tFUkFUSU5JWkFUSU9OIiAKdGhlX2dzX2NvbnRlbnQgPSBnZW5lX3NldHMkZ2VuZV9zZXRzX2Z1bGwgJT4lCiAgZHBseXI6OmZpbHRlcihnc19uYW1lID09IHRoZV9nc19uYW1lKSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX3N5bWJvbCkgJT4lCiAgdW5saXN0KCkKCnNvYmpfaWJsb3JzJHNjb3JlX2tlcmEgPSBTZXVyYXQ6OkFkZE1vZHVsZVNjb3JlKHNvYmpfaWJsb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGxpc3QodGhlX2dzX2NvbnRlbnQpKSRDbHVzdGVyMQoKU2V1cmF0OjpWbG5QbG90KHNvYmpfaWJsb3JzLCBmZWF0dXJlcyA9ICJzY29yZV9rZXJhIiwgcHQuc2l6ZSA9IDAuMDUsCiAgICAgICAgICAgICAgICBzcGxpdC5ieSA9ICJzYW1wbGVfdHlwZSIsIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsCiAgICAgICAgICAgICAgICBjb2xzID0gcmV2KHNhbXBsZV90eXBlX2NvbG9ycykpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKYGBge3IgZmlnX2libG9yc19pZm5hX3Njb3JlLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KdGhlX2dzX25hbWUgPSAiSEFMTE1BUktfSU5URVJGRVJPTl9HQU1NQV9SRVNQT05TRSIgCnRoZV9nc19jb250ZW50ID0gZ2VuZV9zZXRzJGdlbmVfc2V0c19mdWxsICU+JQogIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSA9PSB0aGVfZ3NfbmFtZSkgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9zeW1ib2wpICU+JQogIHVubGlzdCgpCgpzb2JqX2libG9ycyRzY29yZV9pZm5hID0gU2V1cmF0OjpBZGRNb2R1bGVTY29yZShzb2JqX2libG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBsaXN0KHRoZV9nc19jb250ZW50KSkkQ2x1c3RlcjEKClNldXJhdDo6VmxuUGxvdChzb2JqX2libG9ycywgZmVhdHVyZXMgPSAic2NvcmVfaWZuYSIsIHB0LnNpemUgPSAwLjA1LAogICAgICAgICAgICAgICAgc3BsaXQuYnkgPSAic2FtcGxlX3R5cGUiLCBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLAogICAgICAgICAgICAgICAgY29scyA9IHJldihzYW1wbGVfdHlwZV9jb2xvcnMpKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCgpWaW9saW4gcGxvdCBmb3IgSUJMIDoKCmBgYHtyIGZpZ19pYmxvcnNfaWJsLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDIuNX0Kc3Vic29iaiA9IHN1YnNldChzb2JqX2libG9ycywgY2x1c3Rlcl90eXBlID09ICJJQkwiKQoKU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwgcHQuc2l6ZSA9IDAuMDUsCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGMoIkRVU1AxIiwgIkRESVQ0IiwgIk1JRiIsICJMR0FMUzciLCAiQVJGNSIsICJTMTAwQTkiKSwKICAgICAgICAgICAgICAgIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMsIG5jb2wgPSA2KSAmCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKVmlvbGluIHBsb3QgZm9yIE9SUyA6CgpgYGB7ciBmaWdfaWJsb3JzX29ycywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyLjV9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYmxvcnMsIGNsdXN0ZXJfdHlwZSA9PSAiT1JTIikKClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIHB0LnNpemUgPSAwLjA1LAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJEVVNQMSIsICJLTEY2IiwgIkNMRE4xIiwgIkNUR0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTMTAwQTkiLCAiQ0NMMiIsICJJRklUTTMiLCAiSUZJMjciKSwKICAgICAgICAgICAgICAgIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMsIG5jb2wgPSA4KSAmCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKU3BsaXQgYnkgc2FtcGxlIDoKCmBgYHtyIGZpZ19pYmxvcnNfb3JzX3NwbGl0LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIklGSTI3IiwgY29scyA9IHNhbXBsZV9pbmZvJGNvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpIZWF0bWFwIGZvciBjbHVzdGVyIDUgdnMgb3RoZXIgT1JTIDoKCmBgYHtyIGZpZ19pYmxvcnNfaGVhdG1hcF9jbHVzdGVyNSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxOSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYmxvcnMsIGNsdXN0ZXJfdHlwZSA9PSAiT1JTIikKCmZlYXR1cmVzX29pID0gYygiWUJYMyIsICJUWE5JUCIsICJLUlQxNCIsICJLUlQxNSIsICJORUFUMSIsCiAgICAgICAgICAgICAgICAiRlhZRDMiLCAiTVQyQSIsICJNVDFFIiwgIk1UMVgiLCAiQVFQMyIsICJHTFVMIiwKICAgICAgICAgICAgICAgICMgIkhBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCIgogICAgICAgICAgICAgICAgIkZPUyIsICJKVU5CIiwgIkRVU1AxIiwgIlpGUDM2IiwgIk5GS0JJWiIsCiAgICAgICAgICAgICAgICAiQVRGMyIsICJSSE9CIiwgICJFVFMyIiwgIklMMTgiLCAiS0xGNCIsICJLTEY2IiwgIktMRjkiLAogICAgICAgICAgICAgICAgIktMRjMiLCAiS0xGNSIsICJDT0wxN0ExIiwgIlRIU0Q0IiwgIldOVDMiLCAiV05UNCIsICJTTFBJIiwgIlBMQVQiLAogICAgICAgICAgICAgICAgIkxBTUI0IiwgIkRDTiIsICJTUElOSzUiLAogICAgICAgICAgICAgICAgIkdTVE0zIiwgIkFMREgzQTEiLCAgIkxHQUxTN0IiLCAiU0xDMzhBMiIsICJFSEYiLCAgIkNMRUMyQiIsCiAgICAgICAgICAgICAgICAiSUwyMFJCIiwgIklMMVIyIiwgIklGSTI3IiwgIkNYQ0wxNCIsICJITEEtQyIsICJHUFNNMiIsICJEQUFNMSIsICAgIklEMSIsCiAgICAgICAgICAgICAgICAiUk5BU0VUMiIsICJIT1BYIiwgIlBPVTNGMSIsICJTUFJZMSIsICJBUiIsICJQREdGQyIsCiAgICAgICAgICAgICAgICAiV0ZEQzIiLCAiV0ZEQzUiLCAiVFNDMjJEMyIsICJGR0ZSMyIsICAiTFk2RCIsICJJR0ZCUDMiLCAKICAgICAgICAgICAgICAgICMgT3RoZXIgT1JTCiAgICAgICAgICAgICAgICAiQVBPRSIsICJDVFNCIiwgIkNBTEQxIiwgIlNPWDQiLAogICAgICAgICAgICAgICAgIlNUTU4xIiwgIkxNTzQiLCAiQ0VCUEIiLCAiVE1FTTQ1QSIsICJHUFgyIiwgIkMxUVRORjEyIiwgIkdKQjYiLAogICAgICAgICAgICAgICAgIktSVDZBIiwgIktSVDE3IiwgIlJCUDEiLCAiQ0FMTUwzIiwgIlBUTiIsICJEQVBLMiIsCiAgICAgICAgICAgICAgICAiRUdMTjMiLCAiRklMSVAxTCIsICJBREdSTDMiLCAiRlNUIiwgIkVGTkIyIiwgIlNFTUE1QSIsCiAgICAgICAgICAgICAgICAiRkdGUjEiLCAiRUdSMiIsICJDTEROMSIsICJERUZCMSIsICJDQVJEMTgiLCAiTUdTVDEiKQoKIyBNYXRyaXgKbWF0X2V4cHIgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqKQptYXRfZXhwciA9IG1hdF9leHByW2ZlYXR1cmVzX29pLCBdCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQptYXRfZXhwciA9IGR5bnV0aWxzOjpzY2FsZV9xdWFudGlsZShtYXRfZXhwcikgIyBiZXR3ZWVuIDAgYW5kIDEKbWF0X2V4cHIgPSBNYXRyaXg6OnQobWF0X2V4cHIpCmRpbShtYXRfZXhwcikgIyBnZW5lcyB4IGNlbGxzCgojIyBDb2xvcnMKbGlzdF9jb2xvcnMgPSBsaXN0KCkKbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dID0gcmV2KFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIlJkQnUiLCBuID0gOSkpCmxpc3RfY29sb3JzW1sic2FtcGxlX3R5cGUiXV0gPSBzYW1wbGVfdHlwZV9jb2xvcnMKbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXSA9IHNldE5hbWVzKG5tID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaW5mbyRjb2xvcikKbGlzdF9jb2xvcnNbWyJwb3B1bGF0aW9uIl1dID0gc2V0TmFtZXMobm0gPSBjKCJJTDFSMisgT1JTIiwgIm90aGVyIE9SUyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJibGFjayIsIGNvbG9yX21hcmtlcnNbIk9SUyJdKSkKbGlzdF9jb2xvcnNbWyJuRmVhdHVyZV9STkEiXV0gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihicmVha3MgPSBzZXEoZnJvbSA9IG1pbihzdWJzb2JqJG5GZWF0dXJlX1JOQSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gbWF4KHN1YnNvYmokbkZlYXR1cmVfUk5BKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIkdyZXlzIiwgbiA9IDkpKQoKIyBDZWxscyBvcmRlcgpjb2x1bW5fb3JkZXIgPSBzdWJzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6bXV0YXRlKHNldXJhdF9jbHVzdGVycyA9IGZhY3RvcihzZXVyYXRfY2x1c3RlcnMsIGxldmVscyA9IGMoNSwgMywgMCwgMSwgNykpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShzYW1wbGVfdHlwZSwgc2V1cmF0X2NsdXN0ZXJzLCBzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgcm93bmFtZXMoKQpjb2x1bW5fb3JkZXIgPSBtYXRjaChjb2x1bW5fb3JkZXIsIHJvd25hbWVzKHN1YnNvYmpAbWV0YS5kYXRhKSkKCiMgQW5ub3RhdGlvbgpoYV90b3AgPSBIZWF0bWFwQW5ub3RhdGlvbihzYW1wbGVfdHlwZSA9IHN1YnNvYmokc2FtcGxlX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gc3Vic29iaiRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiA9IGlmZWxzZShzdWJzb2JqJGNsdXN0ZXJfdHlwZV9zZXA1ID09ICJPUlMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJvdGhlciBPUlMiLCBubyA9ICJJTDFSMisgT1JTIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3Qoc2FtcGxlX3R5cGUgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gbGlzdF9jb2xvcnNbWyJwb3B1bGF0aW9uIl1dKSkKCmhhX2JvdHRvbSA9IEhlYXRtYXBBbm5vdGF0aW9uKG5GZWF0dXJlX1JOQSA9IHN1YnNvYmokbkZlYXR1cmVfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KG5GZWF0dXJlX1JOQSA9IGxpc3RfY29sb3JzW1sibkZlYXR1cmVfUk5BIl1dKSkKCiMgSGVhdG1hcApodCA9IEhlYXRtYXAoYXMubWF0cml4KG1hdF9leHByKSwKICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJFeHByZXNzaW9uIiwgYXQgPSBjKDAsIDEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJsb3ciLCAiaGlnaCIpKSwKICAgICAgICAgICAgIGNvbCA9IGxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSwKICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfdG9wLAogICAgICAgICAgICAgYm90dG9tX2Fubm90YXRpb24gPSBoYV9ib3R0b20sCiAgICAgICAgICAgICAjIENlbGwgZ3JvdXBpbmcKICAgICAgICAgICAgIGNvbHVtbl9zcGxpdCA9IHN1YnNvYmokc2FtcGxlX3R5cGUgJT4lIGFzLmNoYXJhY3RlcigpLAogICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICBjb2x1bW5fb3JkZXIgPSBjb2x1bW5fb3JkZXIsCiAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSBOVUxMLAogICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IEZBTFNFLAogICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICMgR2VuZXMKICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDE0LCBmb250ZmFjZSA9ICJwbGFpbiIpLAogICAgICAgICAgICAgIyBTdHlsZQogICAgICAgICAgICAgdXNlX3Jhc3RlciA9IEZBTFNFLAogICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICBib3JkZXIgPSBUUlVFKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAicmlnaHQiLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gInJpZ2h0IikKYGBgCgpHZW5lcyBvZiBpbnRlcmVzdCA6CgpgYGB7ciBmaWdfaWJsb3JzX2dlbmVzLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0KZ2VuZXMgPSBjKCJLUlQxNiIsICJDT0wxN0ExIiwgIkRTVCIsICJLUlQ2QiIsICJJTDFSMiIsICJXTlQzIiwKICAgICAgICAgICJJRkkyNyIsICJDWENMMTQiLCAiSUdGQlAzIiwgIktSVDE1IiwgIkNEMjAwIiwgIklMMTgiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpsZW5ndGgoZ2VuZXMpKSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9pZCkgewogIGdlbmUgPSBnZW5lc1tbZ2VuZV9pZF1dCiAgCiAgc29ial9pYmxvcnMkbXlfZ2VuZSA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmpfaWJsb3JzLCBnZW5lKVssIDFdICU+JQogICAgYXF1YXJpdXM6OnJ1bl9yZXNjYWxlKC4sIG5ld19taW4gPSAwLCBuZXdfbWF4ID0gMTApCiAgCiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqX2libG9ycywgZmVhdHVyZXMgPSAibXlfZ2VuZSIsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuMjUpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMTAsIGJ5ID0gMi41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJtaW4iLCByZXAoIiIsIDMpLCAibWF4IikpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE3KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwbG90X2xpc3QKYGBgCgoKIyBIRlNDcyB0byBJQkwgYW5kIE9SUwoKIyMgU2V0dGluZ3MKCldlIGxvYWQgdGhlIG1lcmdlZCBkYXRhc2V0IDoKCmBgYHtyIHNvYmpfdHJhan0Kc29ial90cmFqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzRfem9vbV9oZnNjX2libG1vcnMvaGZzY19pYmxtb3JzX3NvYmpfdHJhal90aW5nYS5yZHMiKSkKc29ial90cmFqCmBgYAoKClRoaXMgaXMgdGhlIHByb2plY3Rpb24gbmFtZSB0byB2aXN1YWxpemUgY2VsbHMgOgoKYGBge3Igc29ial9uYW1lMkRfdHJhan0KbmFtZTJEID0gImhhcm1vbnlfZG0iCmBgYAoKV2UgbG9hZCB0aGUgdHJhamVjdG9yeSBvYmplY3QgZm9yIHZpc3VhbGlzYXRpb24gcHVycG9zZSA6CgpgYGB7ciBsb2FkX3RyYWp9Cm15X3RyYWogPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vNF96b29tX2hmc2NfaWJsbW9ycy9oZnNjX2libG1vcnNfbXlfdHJhal90aW5nYS5yZHMiKSkKY2xhc3MobXlfdHJhaikKYGBgCgoKIyMgUHJlcGFyYXRpb24KCldlIGRlZmluZWQgY2VsbCB0eXBlIGJhc2VkIG9uIGluZGl2aWR1YWwgb2JqZWN0IDoKCmBgYHtyIGNsdXN0ZXJfdHlwZV90cmFqLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0Kc29ial9pYmxvcnMkY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmpfaWJsb3JzKQpzb2JqX3RyYWokY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmpfdHJhaikKc29ial90cmFqJGNsdXN0ZXJfdHlwZSA9IGRwbHlyOjpsZWZ0X2pvaW4oc29ial90cmFqQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAicGVyY2VudC5tdCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ial9pYmxvcnNAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJjbHVzdGVyX3R5cGUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImNlbGxfYmMiKVssICJjbHVzdGVyX3R5cGUiXSAlPiUgYXMuY2hhcmFjdGVyKCkKc29ial90cmFqJGNsdXN0ZXJfdHlwZSA9IGlmZWxzZShjb2xuYW1lcyhzb2JqX3RyYWopICVpbiUgY29sbmFtZXMoc29ial9oZnNjKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiSEZTQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSBzb2JqX3RyYWokY2x1c3Rlcl90eXBlKSAlPiUKICBhcy5mYWN0b3IoKQpgYGAKCiMjIEZpZ3VyZXMKCkNlbGxzIG9uIHRoZSBmdWxsIGF0bGFzIDoKCmBgYHtyIGZpZ190cmFqX2xvY2F0aW9uLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0Kc29iaiRjZWxsX2JjID0gY29sbmFtZXMoc29iaikKc29ial90cmFqJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqX3RyYWopCnNvYmokaXNfdHJhaiA9IGRwbHlyOjpsZWZ0X2pvaW4oc29iakBtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgInBlcmNlbnQubXQiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ial90cmFqQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAiY2x1c3Rlcl90eXBlIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImNlbGxfYmMiKVssICJjbHVzdGVyX3R5cGUiXQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfdHJhaiIsIG9yZGVyID0gbGV2ZWxzKHNvYmpfdHJhaiRjbHVzdGVyX3R5cGUpKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoY29sb3JfbWFya2Vyc1tjKCJJQkwiLCAiT1JTIiwgIkhGU0MiKV0sIGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiSUJMIiwgIk9SUyIsICJIRlNDIiwgTkEpLCBuYS52YWx1ZSA9IGJnX2NvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgoKUHJvamVjdCBuYW1lIDoKCmBgYHtyIGZpZ190cmFqX3Byb2plY3RfbmFtZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CiMgUmFuZG9tIG9yZGVyCnNldC5zZWVkKDEyMzQpCnJuZF9vcmRlciA9IHNhbXBsZShjb2xuYW1lcyhzb2JqX3RyYWopLCByZXBsYWNlID0gRkFMU0UsIHNpemUgPSBuY29sKHNvYmpfdHJhaikpCgojIEV4dHJhY3QgY29vcmRpbmF0ZXMKY2VsbHNfY29vcmQgPSBzb2JqX3RyYWpAcmVkdWN0aW9uc1tbbmFtZTJEXV1AY2VsbC5lbWJlZGRpbmdzICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiRGltMSIsICJEaW0yIikpCmNlbGxzX2Nvb3JkJHNhbXBsZV90eXBlID0gc29ial90cmFqJHNhbXBsZV90eXBlCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbb3JkZXIoc29ial90cmFqJHNhbXBsZV90eXBlKSwgXQoKIyBQbG90CmdncGxvdDI6OmdncGxvdChjZWxsc19jb29yZCwgYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sID0gc2FtcGxlX3R5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMS4yKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV90eXBlX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSkgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKCkNsdXN0ZXIgdHlwZSA6CgpgYGB7ciBmaWdfdHJhal9jbHVzdGVyX3R5cGUsIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQgPSAzfQpTZXVyYXQ6OkRpbVBsb3Qoc29ial90cmFqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpQc2V1ZG90aW1lIDoKCmBgYHtyIGZpZ190cmFqX3BzZXVkb3RpbWUsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmpfdHJhaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gInBzZXVkb3RpbWUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHZpcmlkaXM6OnZpcmlkaXMobiA9IDEwMCkpICsKICBnZ3Bsb3QyOjpsaW1zKHggPSByYW5nZShzb2JqX3RyYWpAcmVkdWN0aW9uc1tbbmFtZTJEXV1AY2VsbC5lbWJlZGRpbmdzWywgMV0pLAogICAgICAgICAgICAgICAgeSA9IHJhbmdlKHNvYmpfdHJhakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3NbLCAyXSkpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICBTZXVyYXQ6Ok5vQXhlcygpCmBgYAoKUHNldWRvdGltZSB3aXRoIGR5bnBsb3QncyBmdW5jdGlvbiA6CgpgYGB7ciBmaWdfdHJhal9wc2V1ZG90aW1lX2R5bnBsb3QsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQpkeW5wbG90OjpwbG90X2RpbXJlZCh0cmFqZWN0b3J5ID0gbXlfdHJhaiwKICAgICAgICAgICAgICAgICAgICAgZGltcmVkID0gc29ial90cmFqW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3MsCiAgICAgICAgICAgICAgICAgICAgICMgQ2VsbHMKICAgICAgICAgICAgICAgICAgICAgY29sb3JfY2VsbHMgPSAncHNldWRvdGltZScsCiAgICAgICAgICAgICAgICAgICAgIHNpemVfY2VsbHMgPSAxLjYsCiAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9yYWRpdXNfcGVyY2VudGFnZSA9IDAsCiAgICAgICAgICAgICAgICAgICAgICMgVHJhamVjdG9yeQogICAgICAgICAgICAgICAgICAgICBwbG90X3RyYWplY3RvcnkgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBjb2xvcl90cmFqZWN0b3J5ID0gIm5vbmUiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbF9taWxlc3RvbmVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIHNpemVfbWlsZXN0b25lcyA9IDAsCiAgICAgICAgICAgICAgICAgICAgIHNpemVfdHJhbnNpdGlvbnMgPSAxKQpgYGAKCiMgT0VQMDAyMzIxIGRhdGFzZXQKCiMjIFNldHRpbmdzCgpXZSBsb2FkIHRoZSBkYXRhc2V0IGNvbnRhaW5pbmcgYWxsIGNlbGxzIDoKCmBgYHtyIGxvYWRfc29ial93dX0Kc29iaiA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzVfd3UvM19jb21iaW5lZC93dV9zb2JqLnJkcyIpKQpzb2JqCmBgYAoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBuYW1lIHRvIHZpc3VhbGl6ZSBjZWxscyA6CgpgYGB7ciBhbGxfc29ial9uYW1lMkRfd3V9Cm5hbWUyRCA9ICJoYXJtb255XzM4X3RzbmUiCm5hbWUyRF9hdGxhcyA9IG5hbWUyRApgYGAKClRoZXNlIGFyZSBhbGwgdGhlIHNhbXBsZXMgYW5hbHl6ZWQgOgoKYGBge3Igc2FtcGxlX2luZm9fd3UsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gM30Kc2FtcGxlX2luZm8gPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi81X3d1LzFfbWV0YWRhdGEvd3Vfc2FtcGxlX2luZm8ucmRzIikpCgojIE5iIGNlbGxzIGJ5IGRhdGFzZXQKdG9fcGxvdCA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoLiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBgY29sbmFtZXM8LWAoYygic2FtcGxlX2lkZW50aWZpZXIiLCAibmJfY2VsbHMiKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbih4ID0gLiwgeSA9IHNhbXBsZV9pbmZvLCBieSA9ICJzYW1wbGVfaWRlbnRpZmllciIpIAoKIyBwYXRjaHdvcmsKcGxvdF9saXN0ID0gYXF1YXJpdXM6OmZpZ19wbG90X2diKHRvX3Bsb3QsIHRpdGxlID0gIkF2YWlsYWJsZSBkYXRhc2V0cyIpCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QpICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGRlc2lnbiA9ICJBXG5CIiwgaGVpZ2h0cyA9IGMoMC4xLDUpKSAmCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE1KSkKYGBgCgpXZSBkZWZpbmUgY2x1c3RlciB0eXBlIGFuZCBjbHVzdGVyIGZhbWlseSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfZmFtaWx5X3d1LCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0Kc29iaiRjZWxsX3R5cGUgPSBzb2JqJGNlbGxfdHlwZSAlPiUKICBhcy5jaGFyYWN0ZXIoKSAlPiUKICBmYWN0b3IoLiwgbGV2ZWxzID0gbmFtZXMoY29sb3JfbWFya2VycykpCgpjbHVzdGVyX3R5cGUgPSB0YWJsZShzb2JqJGNlbGxfdHlwZSwgc29iaiRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjbHVzdGVyX3R5cGUgPSBzZXROYW1lcyhubSA9IG5hbWVzKGNsdXN0ZXJfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhzb2JqJGNlbGxfdHlwZSlbY2x1c3Rlcl90eXBlXSkKCnNvYmokY2x1c3Rlcl90eXBlID0gY2x1c3Rlcl90eXBlW3NvYmokc2V1cmF0X2NsdXN0ZXJzXQpzb2JqJGNsdXN0ZXJfdHlwZSA9IGZhY3Rvcihzb2JqJGNsdXN0ZXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbGV2ZWxzKHNvYmokY2VsbF90eXBlKSkgJT4lCiAgYmFzZTo6ZHJvcGxldmVscygpCnNvYmokY2x1c3Rlcl9mYW1pbHkgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlW3NvYmokY2x1c3Rlcl90eXBlLCAiY2VsbF9mYW1pbHkiXQpzb2JqJGNsdXN0ZXJfZmFtaWx5ID0gZmFjdG9yKHNvYmokY2x1c3Rlcl9mYW1pbHksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbmFtZXMoZmFtaWx5X2NvbG9yKSkKYGBgCgoKIyMgR2xvYmFsIGZpZ3VyZXMKClByb2plY3QgbmFtZSA6CgpgYGB7ciBmaWdzMl9zYW1wbGVfaWRlbnRpZmllciwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CiMgUmFuZG9tIG9yZGVyCnNldC5zZWVkKDEyMzQpCnJuZF9vcmRlciA9IHNhbXBsZShjb2xuYW1lcyhzb2JqKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpAcmVkdWN0aW9uc1tbbmFtZTJEX2F0bGFzXV1AY2VsbC5lbWJlZGRpbmdzICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiRGltMSIsICJEaW0yIikpCmNlbGxzX2Nvb3JkJHByb2plY3RfbmFtZSA9IHNvYmokcHJvamVjdF9uYW1lCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHByb2plY3RfbmFtZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKQ2VsbCB0eXBlIGFubm90YXRpb24gOgoKYGBge3IgZmlnczJfY2VsbF90eXBlLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNsdXN0ZXIgdHlwZSBhbm5vdGF0aW9uIDoKCmBgYHtyIGZpZ3MyX2NsdXN0ZXJfdHlwZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpHZW5lIGV4cHJlc3Npb24gdG8gYXNzZXNzIGFubm90YXRpb24gOgoKYGBge3IgZmlnczJfZ2VuZV9mYW1pbHksIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpnZW5lcyA9IGMoIlBUUFJDIiwgIk1TWDIiLCAiS1JUMTQiKQpuYW1lcyhnZW5lcykgPSBjKCJpbW11bmUgY2VsbHMiLCAibWF0cml4IGNlbGxzIiwgIm5vbi1tYXRyaXggY2VsbHMiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpsZW5ndGgoZ2VuZXMpKSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9pZCkgewogIGdlbmUgPSBnZW5lc1tbZ2VuZV9pZF1dCiAgcG9wID0gbmFtZXMoZ2VuZXMpW2dlbmVfaWRdCiAgCiAgc29iaiRteV9nZW5lID0gU2V1cmF0OjpGZXRjaERhdGEoc29iaiwgZ2VuZSlbLCAxXSAlPiUKICAgIGFxdWFyaXVzOjpydW5fcmVzY2FsZSguLCBuZXdfbWluID0gMCwgbmV3X21heCA9IDEwKQogIAogIFNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgZmVhdHVyZXMgPSAibXlfZ2VuZSIsIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcykgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxMCwgYnkgPSAyLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIm1pbiIsIHJlcCgiIiwgMyksICJtYXgiKSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IGdlbmUpICsgCiAgICAjIHN1YnRpdGxlID0gcG9wKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE3KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwbG90X2xpc3QKYGBgCgpEb3RwbG90IDoKCmBgYHtyIGZpZ3MyX2RvdHBsb3QsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KY3VzdG9tX29yZGVyX2NlbGxfdHlwZSA9IGN1c3RvbV9vcmRlcl9jZWxsX3R5cGVbbGV2ZWxzKHNvYmokY2x1c3Rlcl90eXBlKSwgYygiY2VsbF90eXBlIiwgImNlbGxfZmFtaWx5IildCgpwbG90X2xpc3QgPSBTZXVyYXQ6OkRvdFBsb3Qoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiUFRQUkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDNFIiwgIkNENCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEMjA3IiwgIkFJRjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgIlBSRE0xIiwgIktSVDg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTVNYMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDMyIiwgIktSVDM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUMzEiLCAiUFJSOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJBTUJJIiwgIkFMREgxQTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQ3MSIsICJLUlQ3MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRPUDJBIiwgIk1DTTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNCIsICJDWENMMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNSIsICJDT0wxN0ExIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRElPMiIsICJUQ0VBTDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNiIsICJLUlQ2QyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNQSU5LNSIsICJMWTZEIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBzY2FsZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS5ieSA9ICJyYWRpdXMiLCBzY2FsZS5taW4gPSBOQSwgc2NhbGUubWF4ID0gTkEpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwKICAgICAgICAgICAgICAgICAjIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmJveCA9ICJob3Jpem9udGFsIiwKICAgICAgICAgICAgICAgICBsZWdlbmQuYm94Lm1hcmdpbiA9IG1hcmdpbigwLDI1LDAsMCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTEsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwwLjUsMCwwKSwgImNtIikpCgpwID0gZ2dwbG90Mjo6Z2dwbG90KGN1c3RvbV9vcmRlcl9jZWxsX3R5cGUsIGFlcyh5ID0gY2VsbF90eXBlLCB4ID0gMCkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwKSArCiAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGFlcyh5ID0gMC41LCB5ZW5kID0gMi41LCB4ID0gMCwgeGVuZCA9IDApLCBzaXplID0gNiwgY29sID0gZmFtaWx5X2NvbG9yWyJpbW11bmUgY2VsbHMiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeSA9IDIuNSwgeWVuZCA9IDcuNSwgeCA9IDAsIHhlbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGZhbWlseV9jb2xvclsibWF0cml4Il0pICsKICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoYWVzKHkgPSA3LjUsIHllbmQgPSAxMS41LCB4ID0gMCwgeGVuZCA9IDApLCBzaXplID0gNiwgY29sID0gZmFtaWx5X2NvbG9yWyJub24gbWF0cml4Il0pICsKICBnZ3Bsb3QyOjpzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApLCBsaW1pdHMgPSBjKDAsMCkpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMCwwLDAuNSksICJjbSIpKQoKcGxvdF9saXN0ID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHAsIHBsb3RfbGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSAxLCB3aWR0aHMgPSBjKDEsIDI1KSkKcGxvdF9saXN0CmBgYAoKIyMgSUJMIGFuZCBPUlMgZGF0YXNldAoKV2UgbG9hZCB0aGUgSUJMICsgT1JTIGRhdGFzZXQgOgoKYGBge3Igc29ial9pYmxvcnNfd3V9CnNvYmpfaWJsb3JzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNV93dS80X2libF9vcnMvaWJsbW9yc19zb2JqLnJkcyIpKQpzb2JqX2libG9ycwpgYGAKCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIHNvYmpfbmFtZTJEX2libG9yc193dX0KbmFtZTJEID0gImhhcm1vbnlfMjBfdHNuZSIKYGBgCgpUbyByZXByZXNlbnQgcmVzdWx0cyBmcm9tIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB3ZSBsb2FkIHRoZSBhbmFseXNlcyByZXN1bHRzIDoKCmBgYHtyIGxpc3RfcmVzdWx0c19pYmxvcnNfd3V9Cmxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzVfd3UvNF9pYmxfb3JzL2libG1vcnNfbGlzdF9yZXN1bHRzLnJkcyIpKQoKbGFwcGx5KGxpc3RfcmVzdWx0cywgRlVOID0gbmFtZXMpCmBgYAoKV2UgZGVmaW5lZCBjbHVzdGVyIHR5cGUgOgoKYGBge3IgY2x1c3Rlcl90eXBlX2libG9yc193dSwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDV9CmNsdXN0ZXJfdHlwZSA9IHRhYmxlKHNvYmpfaWJsb3JzJGNlbGxfdHlwZSwgc29ial9pYmxvcnMkc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKY2x1c3Rlcl90eXBlID0gc2V0TmFtZXMobm0gPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoc29ial9pYmxvcnMkY2VsbF90eXBlKVtjbHVzdGVyX3R5cGVdKQoKc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlID0gY2x1c3Rlcl90eXBlW3NvYmpfaWJsb3JzJHNldXJhdF9jbHVzdGVyc10Kc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlID0gZmFjdG9yKHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIklCTCIsICJPUlMiKSkKYGBgCgojIyBJQkwgKyBPUlMgZmlndXJlCgpJQkwgKyBPUlMgb24gdGhlIGZ1bGwgYXRsYXMgOgoKYGBge3IgZmlnczJfaWJsb3JzX2xvY2F0aW9uLCBmaWcud2lkdGggPSAxLjUsIGZpZy5oZWlnaHQgPSAxLjV9CnNvYmokY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmopCnNvYmpfaWJsb3JzJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqX2libG9ycykKc29iaiRpc19pYmxvcnMgPSBkcGx5cjo6bGVmdF9qb2luKHNvYmpAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJwZXJjZW50Lm10IildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ial9pYmxvcnNAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJjbHVzdGVyX3R5cGUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJjZWxsX2JjIilbLCAiY2x1c3Rlcl90eXBlIl0KClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkRfYXRsYXMsIHB0LnNpemUgPSAwLjAwMDAwMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImlzX2libG9ycyIsIG9yZGVyID0gRkFMU0UpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhjb2xvcl9tYXJrZXJzW2MoIklCTCIsICJPUlMiKV0sIGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiSUJMIiwgIk9SUyIsIE5BKSwgbmEudmFsdWUgPSBiZ19jb2xvcikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDbHVzdGVyIHR5cGUgOgoKYGBge3IgZmlnczJfaWJsb3JzX2NsdXN0ZXJfdHlwZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqX2libG9ycywgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCgpERSBnZW5lcyBiZXR3ZWVuIElCTCBhbmQgT1JTIDoKCmBgYHtyIGZpZ3MyX2libG9yc19kZV9wb3AsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA2fQptYXJrID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkbWFyawptYXJrJGdlbmVfbmFtZSA9IHJvd25hbWVzKG1hcmspCm1hcmtfbGFiZWwgPSByYmluZCgKICAjIHVwLXJlZ3VsYXRlZCBpbiBJQkwKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IGF2Z19sb2dGQyksCiAgIyB1cC1yZWd1bGF0ZWQgaW4gT1JTCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSAtYXZnX2xvZ0ZDKSwKICAjIHJlcHJlc2VudGF0aXZlIGFuZCBzZWxlY3RpdmUgZm9yIElCTAogIG1hcmsgJT4lIGRwbHlyOjp0b3BfbiguLCBuID0gMjAsIHd0ID0gKHBjdC4xIC0gcGN0LjIpKSwKICAjIHJlcHJlc2VudGF0aXZlIGFuZCBzZWxlY3RpdmUgZm9yIE9SUwogIG1hcmsgJT4lIGRwbHlyOjp0b3BfbiguLCBuID0gMjAsIHd0ID0gLShwY3QuMSAtIHBjdC4yKSkpICU+JQogIGRwbHlyOjpkaXN0aW5jdCgpCm1hcmtfbGFiZWwgPSBtYXJrX2xhYmVsWyFncmVwbChyb3duYW1lcyhtYXJrX2xhYmVsKSwgcGF0dGVybiA9ICJeTVQiKSwgXQoKYXZnX2xvZ0ZDX3JhbmdlID0gc2V0TmFtZXMoYyhtaW4obWFya19sYWJlbCRhdmdfbG9nRkMpLCAtMSwgMCwgMSwgbWF4KG1hcmtfbGFiZWwkYXZnX2xvZ0ZDKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5tID0gYygiZG9kZ2VyYmx1ZTQiLCAiZG9kZ2VyYmx1ZTMiLCAiI0I3QjdCNyIsICJmaXJlYnJpY2szIiwgImZpcmVicmljazQiKSkKCgpnZ3Bsb3QyOjpnZ3Bsb3QobWFyaywgYWVzKHggPSBwY3QuMSwgeSA9IHBjdC4yLCBjb2wgPSBhdmdfbG9nRkMpKSArCiAgZ2dwbG90Mjo6Z2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwLCBsdHkgPSAyKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSBtYXJrX2xhYmVsLCBtYXgub3ZlcmxhcHMgPSBJbmYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IHBjdC4xLCB5ID0gcGN0LjIsIGxhYmVsID0gZ2VuZV9uYW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9ICJibGFjayIsIGZpbGwgPSBOQSwgc2l6ZSA9IDMuNSwgbGFiZWwuc2l6ZSA9IE5BKSArCiAgZ2dwbG90Mjo6bGFicyh4ID0gIkVucmljaGVkIGluIElCTCIsCiAgICAgICAgICAgICAgICB5ID0gIkVucmljaGVkIGluIE9SUyIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gbmFtZXMoYXZnX2xvZ0ZDX3JhbmdlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gc2NhbGVzOjpyZXNjYWxlKHVubmFtZShhdmdfbG9nRkNfcmFuZ2UpKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKYGBgCgpHU0VBIHBsb3QgOgoKYGBge3IgZmlnczJfaWJsb3JzX2tlcmF0aW5pemF0aW9uLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KdGhlX2dzX25hbWUgPSAiUkVBQ1RPTUVfS0VSQVRJTklaQVRJT04iIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJGdzZWFAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIoSUQgPT0gdGhlX2dzX25hbWUpCnRoZV9zdWJ0aXRsZSA9IHBhc3RlMCgiXG5ORVMgOiAiLCByb3VuZCh0aGVfY29udGVudCRORVMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIiB8IHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiIHwgc2V0IHNpemUgOiAiLCB0aGVfY29udGVudCRzZXRTaXplLCAiIGdlbmVzIikKCmVucmljaHBsb3Q6OmdzZWFwbG90Mih4ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYSwKICAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSB0aGVfc3VidGl0bGUpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDMsIDMsIDUsIDMpKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZ2d0ZXh0OjplbGVtZW50X21hcmtkb3duKGhqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwKSkKYGBgCgpgYGB7ciBmaWdzMl9pYmxvcnNfaWZuYSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIkhBTExNQVJLX0lOVEVSRkVST05fR0FNTUFfUkVTUE9OU0UiIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJGdzZWFAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIoSUQgPT0gdGhlX2dzX25hbWUpCnRoZV9zdWJ0aXRsZSA9IHBhc3RlMCgiXG5ORVMgOiAiLCByb3VuZCh0aGVfY29udGVudCRORVMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIiB8IHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiIHwgc2V0IHNpemUgOiAiLCB0aGVfY29udGVudCRzZXRTaXplLCAiIGdlbmVzIikKCmVucmljaHBsb3Q6OmdzZWFwbG90Mih4ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYSwKICAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSB0aGVfc3VidGl0bGUpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDMsIDMsIDUsIDMpKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZ2d0ZXh0OjplbGVtZW50X21hcmtkb3duKGhqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwKSkKYGBgCgpHZW5lcyBvZiBpbnRlcmVzdCA6CgpgYGB7ciBmaWdzMl9pYmxvcnNfZ2VuZXMsIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpnZW5lcyA9IGMoIktSVDE2IiwgIkNPTDE3QTEiLCAiRFNUIiwgIktSVDZCIiwgIklMMVIyIiwgIldOVDMiLAogICAgICAgICAgIklGSTI3IiwgIkNYQ0wxNCIsICJJR0ZCUDMiLCAiS1JUMTUiLCAiQ0QyMDAiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpsZW5ndGgoZ2VuZXMpKSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9pZCkgewogIGdlbmUgPSBnZW5lc1tbZ2VuZV9pZF1dCiAgCiAgc29ial9pYmxvcnMkbXlfZ2VuZSA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmpfaWJsb3JzLCBnZW5lKVssIDFdICU+JQogICAgYXF1YXJpdXM6OnJ1bl9yZXNjYWxlKC4sIG5ld19taW4gPSAwLCBuZXdfbWF4ID0gMTApCiAgCiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqX2libG9ycywgZmVhdHVyZXMgPSAibXlfZ2VuZSIsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuMjUpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMTAsIGJ5ID0gMi41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJtaW4iLCByZXAoIiIsIDMpLCAibWF4IikpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE3KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwbG90X2xpc3QKYGBgCgojIFN1cHBsZW1lbnRhcnkgdGFibGVzCgpJbiB0aGlzIHNlY3Rpb24sIHdlIHNhdmUgZmlsZXMgdG8gYXNzb2NpYXRlIHdpdGggdGhlIG1hbnVzY3JpcHQsIGFzIHN1cHBsZW1lbnRhcnkgdGFibGVzLgoKIyMgVGFibGUgUzIgKHBhY2thZ2UgdmVyc2lvbikKCldlIGxvYWQgdGhlIHRhYmxlIDoKCmBgYHtyIHRzMl9sb2FkfQpwYWNrYWdlX3ZlcnNpb24gPSByZWFkLnRhYmxlKHBhc3RlMCgiLiIsICIvZGF0YS9pbmZvX3RvX2luc3RhbGxfMjAyM18wNF8xNy50eHQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFKQpoZWFkKHBhY2thZ2VfdmVyc2lvbikKYGBgCgpDb2x1bW5zIGNvcnJlc3BvbmQgdG8gOgoKKiAqKm9yZGVyKiogOiBvcmRlciB0byBpbnN0YWxsIHBhY2thZ2Ugc3VjaCB0aGF0IG9uZSBuZXZlciBuZWVkIHRvIGluc3RhbGwgZGVwZW5kZW5jaWVzCiogKipwYWNrYWdlX25hbWUqKiA6IHBhY2thZ2UgbmFtZQoqICoqdmVyc2lvbioqIDogaW5zdGFsbGVkIHZlcnNpb24sIG9uIHRoZSBsb2NhbCBtYWNoaW5lCiogKip1cmwqKiA6IHRoZSBwYWNrYWdlIHdhcyBpbnN0YWxsZWQgaW4gdGhlIFNpbmd1bGFyaXR5IGNvbnRhaW5lciB1c2luZyB0aGlzIHVybAoKV2Ugc2F2ZSB0aGlzIHRhYmxlLCBleGNlcHQgdGhlIGxhc3QgY29sdW1uIChqdXN0IGZvciBtZSkgOgoKYGBge3IgdHMyX3NhdmV9Cm9wZW54bHN4Ojp3cml0ZS54bHN4KHBhY2thZ2VfdmVyc2lvblssIGMoMTo0KV0sCiAgICAgICAgICAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoIi4iLCAiL2RhdGEvU3VwcGxlbWVudGFyeSBUYWJsZSAyLnhsc3giKSkKYGBgCgojIyBUYWJsZSBTMyAoY2VsbCB0eXBlIGFubm90YXRpb24pCgpXZSBsb2FkIHRoZSB0YWJsZSA6CgpgYGB7ciB0czNfbG9hZH0KY2VsbF9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9jZWxsX21hcmtlcnMucmRzIikpCmxlbmd0aHMoY2VsbF9tYXJrZXJzKQpgYGAKCldlIHNhdmUgdGhpcyB0YWJsZSwgZXhjZXB0IHRoZSBsYXN0IGNvbHVtbiAoanVzdCBmb3IgbWUpIDoKCmBgYHtyIHRzM19zYXZlfQpvcGVueGxzeDo6d3JpdGUueGxzeChjZWxsX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoIi4iLCAiL2RhdGEvU3VwcGxlbWVudGFyeSBUYWJsZSAzLnhsc3giKSkKYGBgCgoKIyBSIFNlc3Npb24KCmBgYHtyIHNlc3Npb25pbmZvLCBlY2hvID0gRkFMU0UsIGZvbGRfb3V0cHV0ID0gVFJVRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==